У меня есть функциональный компонент, и я хочу заставить его повторно отрисоваться.
Как я могу это сделать?
Поскольку экземпляра нет this
, я не могу позвонить this.forceUpdate()
.
У меня есть функциональный компонент, и я хочу заставить его повторно отрисоваться.
Как я могу это сделать?
Поскольку экземпляра нет this
, я не могу позвонить this.forceUpdate()
.
Ответы:
Используя перехватчики реакции, теперь вы можете вызывать 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
. С этой способностью вы можете теперь имитируют A forceUpdate
:
Обратите внимание, что этот подход следует пересмотреть и в большинстве случаев, когда вам нужно принудительно выполнить обновление, вы, вероятно, делаете что-то не так.
Прежде чем реагировать 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)
}
}