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()
.
javascript
reactjs
Gorakh Nath
źródło
źródło
Odpowiedzi:
🎉 Teraz możesz, używając haków React
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> ); }
Możesz znaleźć demo tutaj .
Powyższy komponent używa niestandardowej funkcji podpięcia (
useForceUpdate
), która używa zaczepu stanu reagowaniauseState
. Zwiększa wartość stanu komponentu, a tym samym nakazuje Reactowi ponowne renderowanie komponentu.EDYTOWAĆ
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
useState
seterze (setValue
tutaj)React
porównaj poprzedni stan z nowym i renderuj tylko wtedy, gdy stan jest inny.źródło
forceUpdate
.useState
haku, a nie klasie,setState
któ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ą dosetState
, nie renderuje się ponownie : codeandbox.io/s/determined-rubin-8598lAktualizacja 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ć AforceUpdate
:Pokaż fragment kodu
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"/>
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,
functions
które powracająjsx
, nie masz żadnego dostępu do metod cyklu życia Reacta, ponieważ nie rozszerzasz zReact.Component
.Pomyśl o komponencie funkcyjnym jako o
render
części metodycznej komponentów klasy.źródło
props
lubstate
zmian. nie możesz wymusić funkcji renderującej, ponieważ nie marender
funkcji na składnikach bezstanowych. komponenty bezstanowe, czyżextends
React.Component
nie 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(); }
źródło
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} />; };
źródło
useForceUpdate
zastosowaniauseCallback
wymienione 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:
źródło
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> ) }
źródło
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);
źródło
Żadne z nich nie dało mi satysfakcjonującej odpowiedzi, więc ostatecznie otrzymałem to, co chciałem, z
key
prop, useRef i jakimś generatorem losowych identyfikatorówshortid
.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) } }
źródło