Mam składnik funkcyjny i chcę zmusić go do ponownego renderowania.
Jak mogę to zrobić?
Ponieważ nie ma instancji this, nie mogę zadzwonić this.forceUpdate().
Mam składnik funkcyjny i chcę zmusić go do ponownego renderowania.
Jak mogę to zrobić?
Ponieważ nie ma instancji this, nie mogę zadzwonić this.forceUpdate().
Odpowiedzi:
Używając zaczepów reagowania, możesz teraz wywołać useState()swój komponent funkcji.
useState() zwróci tablicę dwóch rzeczy:
Zaktualizowanie wartości przez jej ustawiającego wymusi ponowne renderowanie komponentu funkcji ,
tak jak 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>
);
}
Powyższy komponent używa niestandardowej funkcji podpięcia ( useForceUpdate), która używa zaczepu stanu reagowania useState. Zwiększa wartość stanu komponentu, a tym samym nakazuje Reactowi ponowne renderowanie komponentu.
W starej wersji tej odpowiedzi fragment kodu używał wartości logicznej i włączał ją forceUpdate(). Teraz, gdy zredagowałem odpowiedź, fragment kodu używa liczby, a nie wartości logicznej.
Czemu ? (zapytałbyś mnie)
Ponieważ kiedyś zdarzyło mi się, że mój forceUpdate()został wywołany dwukrotnie później z 2 różnych zdarzeń, a zatem resetował wartość logiczną do jej pierwotnego stanu, a komponent nigdy się nie renderował.
Dzieje się tak, ponieważ w useStateseterze ( setValuetutaj) Reactporównaj poprzedni stan z nowym i renderuj tylko wtedy, gdy stan jest inny.
forceUpdate.
useStatehaku, a nie klasie, setStatektóra rzeczywiście nie dokonuje porównania (chyba że zaimplementujesz metodę shouldUpdate). Zobacz to samo demo, które opublikowałem, ale ze statyczną wartością używaną do setState, nie renderuje się ponownie : codeandbox.io/s/determined-rubin-8598l
Aktualizacja React v16.8 (realease 16 lutego 2019)
Ponieważ reagują 16.8 wypuszczone z haczykami , komponenty funkcyjne mają teraz zdolność utrzymywania trwałości state. Dzięki tej zdolności można teraz naśladować A forceUpdate:
Zwróć uwagę, że to podejście powinno zostać ponownie rozważone iw większości przypadków, gdy musisz wymusić aktualizację, prawdopodobnie robisz coś źle.
Przed reakcją 16.8.0
Nie, nie możesz, komponenty funkcji bez stanu są po prostu normalne, functionsktóre powracają jsx, nie masz żadnego dostępu do metod cyklu życia Reacta, ponieważ nie rozszerzasz z React.Component.
Pomyśl o komponencie funkcyjnym jako o renderczęści metodycznej komponentów klasy.
propslub statezmian. nie możesz wymusić funkcji renderującej, ponieważ nie ma renderfunkcji na składnikach bezstanowych. komponenty bezstanowe, czyż extends React.Componentnie są to zwykłe funkcje, które zwracają jsx.
Oficjalne FAQ ( https://reactjs.org/docs/hooks-faq.html#is-there-something-like-forceupdate ) teraz zaleca ten sposób, jeśli naprawdę musisz to zrobić:
const [ignored, forceUpdate] = useReducer(x => x + 1, 0);
function handleClick() {
forceUpdate();
}
const [, forceUpdate] = useReducer(x => x + 1, 0);
Użyłem biblioteki innej firmy o nazwie use-force-update, aby wymusić renderowanie komponentów funkcjonalnych reagujących. Działało jak urok. Po prostu użyj importu pakietu w swoim projekcie i używaj w ten sposób.
import useForceUpdate from 'use-force-update';
const MyButton = () => {
const forceUpdate = useForceUpdate();
const handleClick = () => {
alert('I will re-render now.');
forceUpdate();
};
return <button onClick={handleClick} />;
};
useForceUpdatezastosowania useCallbackwymienione w innych odpowiedziach. Ta biblioteka jest po prostu biblioteką narzędziową, która pozwala zaoszczędzić kilka naciśnięć klawiszy.
Zastrzeżenie: NIE ODPOWIEDŹ NA PROBLEM.
Pozostawiając tutaj ważną notatkę:
Jeśli próbujesz wymusić aktualizację składnika bezstanowego, prawdopodobnie jest coś nie tak z Twoim projektem.
Rozważ następujące przypadki:
Można to zrobić bez jawnego używania hooków, pod warunkiem, że dodasz właściwość do komponentu i stan do komponentu nadrzędnego komponentu bezstanowego:
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>
)
}
Przyjęta odpowiedź jest dobra. Żeby było łatwiej zrozumieć.
Przykładowy komponent:
export default function MyComponent(props) {
const [updateView, setUpdateView] = useState(0);
return (
<>
<span style={{ display: "none" }}>{updateView}</span>
</>
);
}
Aby wymusić ponowne renderowanie, wywołaj poniższy kod:
setUpdateView((updateView) => ++updateView);
Żadne z nich nie dało mi satysfakcjonującej odpowiedzi, więc ostatecznie otrzymałem to, co chciałem, z keyprop, useRef i jakimś generatorem losowych identyfikatorów shortid.
Zasadniczo chciałem, aby aplikacja do czatu odegrała się, gdy ktoś po raz pierwszy otworzy aplikację. Tak więc potrzebowałem pełnej kontroli nad tym, kiedy i jakie odpowiedzi są aktualizowane, z łatwością asynchronicznego oczekiwania.
Przykładowy kod:
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>
W rzeczywistości pozwoliło mi to również rzucić coś w rodzaju wiadomości na czacie z przewijaniem.
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)
}
}