javascript - Hvordan bruke componentWillMount() i React Hooks?
I de offisielle dokumentene til React nevnes det -
Hvis du er kjent med React-klassens livssyklusmetoder, kan du tenke på useEffect Hook som en kombinasjon av componentDidMount, componentDidUpdate og componentWillUnmount.
Mitt spørsmål er - hvordan kan vi bruke componentWillMount() livssyklusmetoden i en hook?
Svar #1
Du kan ikke bruke noen av de eksisterende livssyklusmetodene (componentDidMount, componentDidUpdate, componentWillUnmount osv.) i en hook. De kan bare brukes i klassekomponenter. Og med Hooks kan du bare bruke dem i funksjonelle komponenter. Linjen nedenfor kommer fra React-dokumentasjonen:
Hvis du er kjent med React-klassenes livssyklusmetoder, kan du tenke på
useEffect-Hooken som en kombinasjon avcomponentDidMount,componentDidUpdateogcomponentWillUnmount.
Foreslår er at du kan etterligne disse livssyklusmetodene fra klassekomponenter i funksjonelle komponenter.
Kode inne i componentDidMount kjører én gang når komponenten monteres. useEffect-hooken som tilsvarer denne oppførselen er
useEffect(() => {
// Your code here
}, []);
Legg merke til den andre parameteren her (tom matrise). Dette vil kjøre bare én gang.
Uten den andre parameteren vil useEffect-hooken bli kalt ved hver rendering av komponenten, noe som kan være farlig.
useEffect(() => {
// Your code here
});
componentWillUnmount brukes til opprydding (som å fjerne hendelseslyttere, avbryte timere osv.). Si at du legger til en hendelseslytter i componentDidMount og fjerner den i componentWillUnmount som vist nedenfor.
componentDidMount() {
window.addEventListener('mousemove', () => {})
}
componentWillUnmount() {
window.removeEventListener('mousemove', () => {})
}
Hook-ekvivalenten av ovenstående kode vil være som følger
useEffect(() => {
window.addEventListener('mousemove', () => {});
// returned function will be called on component unmount
return () => {
window.removeEventListener('mousemove', () => {})
}
}, [])
Svar #2
useComponentWillMount-hook
const useComponentWillMount = (cb) => {
const willMount = useRef(true)
if (willMount.current) cb()
willMount.current = false
}
Denne kroken kan være en redning når det er et problem med rekkefølgen (for eksempel å kjøre før et annet skript). Hvis det ikke er tilfelle, bruk useComponentDidMount som er mer i tråd med React-hooks-paradigmet.
useComponentDidMount-hook
const useComponentDidMount = cb => useEffect(cb, []);
Hvis du vet at effekten din kun skal kjøre én gang i begynnelsen, bruk denne løsningen. Den vil kjøre kun én gang etter at komponenten har blitt montert.
useEffect-paradigme
Klassekomponenter har livssyklusmetoder som er definert som punkter i komponentens tidslinje. Hooks følger ikke dette paradigmet. I stedet bør effekter struktureres etter innholdet deres.
function Post({postID}){
const [post, setPost] = useState({})
useEffect(()=>{
fetchPosts(postID).then(
(postObject) => setPost(postObject)
)
}, [postID])
...
}
I eksemplet ovenfor håndterer effekten innhenting av innholdet i et innlegg. I stedet for et bestemt tidspunkt har den en verdi den er avhengig av - postID. Hver gang postID får en ny verdi (inkludert initialisering) vil den kjøre på nytt.
Diskusjon om Component Will Mount
I klassekomponenter anses componentWillMount som foreldet (kilde 1, kilde 2). Det er foreldet siden det kan kjøre mer enn én gang, og det finnes et alternativ - å bruke konstruktøren. Disse vurderingene er ikke relevante for en funksjonell komponent.
Svar #3
I henhold til reactjs.org vil componentWillMount ikke bli støttet i fremtiden. https://reactjs.org/docs/react-component.html#unsafe_componentwillmount
Det er ikke nødvendig å bruke componentWillMount.
Hvis du vil gjøre noe før komponenten monteres, gjør det bare i constructor().
Hvis du vil gjøre nettverksforespørsler, ikke gjør det i componentWillMount. Dette er fordi å gjøre det vil føre til uventede feil.
Nettverksforespørsler kan gjøres i componentDidMount.
Håper det hjelper.
oppdatert 08.03.2019
Grunnen til at du spør om componentWillMount er sannsynligvis fordi du vil initialisere tilstanden før rendering.
Bare gjør det i useState.
const helloWorld=()=>{
const [value,setValue]=useState(0) //initialize your state here
return <p>{value}</p>
}
export default helloWorld;
eller kanskje du vil kjøre en funksjon i componentWillMount, for eksempel hvis koden din opprinnelig ser slik ut:
componentWillMount(){
console.log('componentWillMount')
}
med hook, alt du trenger å gjøre er å fjerne livssyklusmetoden:
const hookComponent=()=>{
console.log('componentWillMount')
return <p>you have transfered componeWillMount from class component into hook </p>
}
Jeg vil bare legge til noe til det første svaret om useEffect.
useEffect(()=>{})
useEffect kjøres på hver gjengivelse, det er en kombinasjon av componentDidUpdate, componentDidMount og ComponentWillUnmount.
useEffect(()=>{},[])
Hvis vi legger til en tom array i useEffect, kjører den bare når komponenten monteres. Det er fordi useEffect vil sammenligne arrayen du passerer til den. Så det trenger ikke å være en tom array. Det kan være en array som ikke endres. For eksempel kan det være [1,2,3] eller ['1,2']. useEffect kjører fortsatt bare når komponenten monteres.
Det avhenger av deg om du vil at det skal kjøre bare én gang eller kjøre etter hver gjengivelse. Det er ikke farlig hvis du glemte å legge til en array så lenge du vet hva du gjør.
Jeg laget et eksempel for krok. Vennligst sjekk det ut.
https://codesandbox.io/s/kw6xj153wr
oppdatert 21.08.2019
Det har gått en stund siden jeg skrev svaret ovenfor. Det er noe jeg tror du trenger å være oppmerksom på. Når du bruker
useEffect(()=>{},[])
Når React sammenligner verdiene du har sendt til arrayet [], bruker det Object.is() for å sammenligne. Hvis du sender et objekt til det, slik som
useEffect(()=>{},[{name:'Tom'}])
Dette er akkurat det samme som:
useEffect(()=>{})
Den vil rendre på nytt hver gang fordi når Object.is() sammenligner et objekt, sammenligner den referansen, ikke selve verdien. Det er det samme som hvorfor {}==={} returnerer false fordi referansene deres er forskjellige. Hvis du fremdeles vil sammenligne selve objektet, ikke referansen, kan du gjøre noe slikt:
useEffect(()=>{},[JSON.stringify({name:'Tom'})])
Oppdatering på 07.09.2021:
Noen oppdateringer om avhengigheten:
Generelt sett, hvis du bruker en funksjon eller et objekt som en avhengighet, vil det alltid gjengi på nytt. Men React gir deg allerede løsningen: useCallback og useMemo
useCallback kan huske en funksjon. useMemo kan huske et objekt.
Se denne artikkelen:
https://javascript.plainenglish.io/5-useeffect-infinite-loop-patterns-2dc9d45a253f
Svar #4
React livssyklusmetoder i hooks
for enkel visuell referanse, følg dette bildet
Som du enkelt kan se på bildet ovenfor, for ComponentWillUnmount må du gjøre det slik
useEffect(() => {
return () => {
console.log('componentWillUnmount');
};
}, []);
Svar #5
Du kan hacke useMemo-hooken for å etterligne en componentWillMount-livssyklus-hendelse. Bare gjør:
const Component = () => {
useMemo(() => {
// componentWillMount events
},[]);
useEffect(() => {
// componentDidMount events
return () => {
// componentWillUnmount events
}
}, []);
};
Du må holde useMemo-hooken før noe som interagerer med tilstanden din. Dette er ikke slik det er ment, men det fungerte for meg med alle componentWillMount-problemer.
Dette fungerer fordi useMemo ikke krever at man faktisk returnerer en verdi, og du trenger ikke å faktisk bruke det som noe, men siden det lagrer en verdi basert på avhengigheter som bare kjører én gang ("[]") og det er øverst i komponenten vår, kjører det én gang når komponenten monteres før noe annet.
Svar #6
useLayoutEffect kunne oppnå dette med et tomt sett av observatører ([]) hvis funksjonaliteten faktisk ligner på componentWillMount -- det vil kjøre før det første innholdet kommer til DOM-en -- selv om det faktisk er to oppdateringer, men de er synkrone før tegning på skjermen.
for eksempel:
function MyComponent({ ...andItsProps }) {
useLayoutEffect(()=> {
console.log('I am about to render!');
},[]);
return (<div>some content</div>);
}
Fordelen over useState med en initializer/setter eller useEffect er at selv om det kan beregne en renderingsrunde, er det ingen faktiske re-rendringer i DOM-en som en bruker vil legge merke til, og det kjøres før den første merkbare renderingen, noe som ikke er tilfelle for useEffect. Ulempen er selvfølgelig en liten forsinkelse i din første rendering siden en sjekk/oppdatering må skje før skjermen males. Det avhenger virkelig av brukstilfellet ditt, though.
Jeg synes personlig at useMemo er greit i noen nisjetilfeller hvor du trenger å gjøre noe tungt -- så lenge du husker at det er unntaket kontra normen.
Svar #7
Dette er måten jeg simulerer konstruktør i funksjonelle komponenter ved å bruke useRef-hooken:
function Component(props) {
const willMount = useRef(true);
if (willMount.current) {
console.log('This runs only once before rendering the component.');
willMount.current = false;
}
return (<h1>Meow world!</h1>);
}
Her er livssyklus-eksemplet:
function RenderLog(props) {
console.log('Render log: ' + props.children);
return (<>{props.children}</>);
}
function Component(props) {
console.log('Body');
const [count, setCount] = useState(0);
const willMount = useRef(true);
if (willMount.current) {
console.log('First time load (it runs only once)');
setCount(2);
willMount.current = false;
} else {
console.log('Repeated load');
}
useEffect(() => {
console.log('Component did mount (it runs only once)');
return () => console.log('Component will unmount');
}, []);
useEffect(() => {
console.log('Component did update');
});
useEffect(() => {
console.log('Component will receive props');
}, [count]);
return (
<>
<h1>{count}</h1>
<RenderLog>{count}</RenderLog>
</>
);
}
[Log] Body
[Log] First time load (it runs only once)
[Log] Body
[Log] Repeated load
[Log] Render log: 2
[Log] Component did mount (it runs only once)
[Log] Component did update
[Log] Component will receive props
Selvfølgelig har klassekomponenter ikke Body-steg, det er ikke mulig å lage en 1:1-simulering på grunn av forskjellige konsepter for funksjoner og klasser.
