У меня есть функциональный компонент, и я хочу заставить его повторно отрисоваться.
Как я могу это сделать?
Поскольку экземпляра нет this
, я не могу позвонить this.forceUpdate()
.
javascript
reactjs
Горакх Натх
источник
источник
Ответы:
🎉 Теперь вы можете, используя хуки React
Используя перехватчики реакции, теперь вы можете вызывать
useState()
свой функциональный компонент.useState()
вернет массив из двух вещей:Обновление значения его установщиком заставит ваш функциональный компонент повторно отрисоваться ,
как это
forceUpdate
делает:import React, { useState } from 'react'; //create your forceUpdate hook function useForceUpdate(){ const [value, setValue] = useState(0); // integer state return () => setValue(value => value + 1); // update the state to force render } function MyComponent() { // call your hook here const forceUpdate = useForceUpdate(); return ( <div> {/*Clicking on the button will force to re-render like force update does */} <button onClick={forceUpdate}> Click to re-render </button> </div> ); }
Вы можете найти демо здесь .
Компонент выше использует настраиваемую функцию перехвата (
useForceUpdate
), которая использует перехватчик состояния реакцииuseState
. Он увеличивает значение состояния компонента и, таким образом, сообщает React о необходимости повторного рендеринга компонента.РЕДАКТИРОВАТЬ
В старой версии этого ответа фрагмент использовал логическое значение и переключал его
forceUpdate()
. Теперь, когда я отредактировал свой ответ, во фрагменте используется число, а не логическое значение.Почему ? (вы бы спросили меня)
Потому что однажды со мной случилось, что my
forceUpdate()
был вызван дважды из двух разных событий, и, таким образом, он сбрасывал логическое значение в исходное состояние, а компонент никогда не отображался.Это связано с тем, что в
useState
сеттере (setValue
здесь)React
сравнивайте предыдущее состояние с новым и визуализируйте только в том случае, если состояние другое.источник
forceUpdate
.useState
ловушке, а не о классе,setState
который действительно не выполняет сравнение (если вы не реализуете метод shouldUpdate). См. Ту же демонстрацию, которую я опубликовал, но со статическим значением, используемым дляsetState
, она больше не отображается: codeandbox.io/s/determined-rubin-8598lОбновление response v16.8 (релиз 16 февраля 2019 г.)
Поскольку response 16.8 выпущен с хуками , функциональные компоненты теперь могут сохранять постоянные
state
. С этой способностью вы можете теперь имитируют AforceUpdate
:Показать фрагмент кода
function App() { const [, updateState] = React.useState(); const forceUpdate = React.useCallback(() => updateState({}), []); console.log("render"); return ( <div> <button onClick={forceUpdate}>Force Render</button> </div> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script> <div id="root"/>
Обратите внимание, что этот подход следует пересмотреть и в большинстве случаев, когда вам нужно принудительно выполнить обновление, вы, вероятно, делаете что-то не так.
Прежде чем реагировать 16.8.0
Нет, вы не можете, функциональные компоненты без состояния - это нормально,
functions
что возвращаетсяjsx
, у вас нет доступа к методам жизненного цикла React, поскольку вы не расширяетесь изReact.Component
.Думайте о функциональном компоненте как о
render
методической части компонентов класса.источник
props
илиstate
изменений. вы не можете принудительно выполнить функцию рендеринга, поскольку дляrender
компонентов без состояния функции нет . Компоненты без состояния, не такextends
React.Component
ли? Это просто возвращаемые функцииjsx
.Официальный FAQ ( https://reactjs.org/docs/hooks-faq.html#is-there-something-like-forceupdate ) теперь рекомендует этот способ, если вам действительно это нужно:
const [ignored, forceUpdate] = useReducer(x => x + 1, 0); function handleClick() { forceUpdate(); }
источник
const [, forceUpdate] = useReducer(x => x + 1, 0);
Я использовал стороннюю библиотеку под названием use-force-update для принудительной визуализации моих функциональных компонентов реакции. Работал как шарм. Просто используйте импорт пакета в своем проекте и используйте его так.
import useForceUpdate from 'use-force-update'; const MyButton = () => { const forceUpdate = useForceUpdate(); const handleClick = () => { alert('I will re-render now.'); forceUpdate(); }; return <button onClick={handleClick} />; };
источник
useForceUpdate
используйте,useCallback
как указано в других ответах. Эта библиотека - всего лишь служебная библиотека, чтобы сэкономить несколько нажатий клавиш.Заявление об ограничении ответственности: НЕ ОТВЕТ НА ПРОБЛЕМУ.
Оставив здесь важное примечание:
Если вы пытаетесь принудительно обновить компонент без состояния, скорее всего, что-то не так с вашим дизайном.
Рассмотрим следующие случаи:
источник
Это можно сделать без явного использования хуков при условии, что вы добавляете опору в свой компонент и состояние родительскому компоненту компонента без состояния:
const ParentComponent = props => { const [updateNow, setUpdateNow] = useState(true) const updateFunc = () => { setUpdateNow(!updateNow) } const MyComponent = props => { return (<div> .... </div>) } const MyButtonComponent = props => { return (<div> <input type="button" onClick={props.updateFunc} />.... </div>) } return ( <div> <MyComponent updateMe={updateNow} /> <MyButtonComponent updateFunc={updateFunc}/> </div> ) }
источник
Принятый ответ хорош. Просто чтобы было легче понять.
Пример компонента:
export default function MyComponent(props) { const [updateView, setUpdateView] = useState(0); return ( <> <span style={{ display: "none" }}>{updateView}</span> </> ); }
Для принудительного повторного рендеринга вызовите приведенный ниже код:
setUpdateView((updateView) => ++updateView);
источник
Ни один из них не дал мне удовлетворительного ответа, поэтому в конце концов я получил то, что хотел, с помощью
key
prop, useRef и некоторого генератора случайных идентификаторов, напримерshortid
.По сути, я хотел, чтобы какое-то приложение для чата запускалось само по себе при первом открытии приложения. Итак, мне нужен был полный контроль над тем, когда и какие ответы обновляются с помощью async await.
Пример кода:
function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // ... your JSX functional component, import shortid somewhere const [render, rerender] = useState(shortid.generate()) const messageList = useRef([ new Message({id: 1, message: "Hi, let's get started!"}) ]) useEffect(()=>{ await sleep(500) messageList.current.push(new Message({id: 1, message: "What's your name?"})) // ... more stuff // now trigger the update rerender(shortid.generate()) }, []) // only the component with the right render key will update itself, the others will stay as is and won't rerender. return <div key={render}>{messageList.current}</div>
Фактически, это также позволило мне прокрутить что-то вроде сообщения чата с прокруткой.
const waitChat = async (ms) => { let text = "." for (let i = 0; i < ms; i += 200) { if (messageList.current[messageList.current.length - 1].id === 100) { messageList.current = messageList.current.filter(({id}) => id !== 100) } messageList.current.push(new Message({ id: 100, message: text })) if (text.length === 3) { text = "." } else { text += "." } rerender(shortid.generate()) await sleep(200) } if (messageList.current[messageList.current.length - 1].id === 100) { messageList.current = messageList.current.filter(({id}) => id !== 100) } }
источник