javascript - Okänt fel: Renderade färre hooks än väntat. Detta kan orsakas av ett oavsiktligt tidigt return-statement i React Hooks
Givet följande komponent, när jag trycker ner på åldersväljaren och ändrar värdet till 15, så att jag renderar ett formulär utan fältet för körkort, får jag felet:
Uncaught Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement.
at invariant (react-dom.development.js:55)
at finishHooks (react-dom.development.js:11581)
at updateFunctionComponent (react-dom.development.js:14262)
at beginWork (react-dom.development.js:15103)
at performUnitOfWork (react-dom.development.js:17817)
at workLoop (react-dom.development.js:17857)
at HTMLUnknownElement.callCallback (react-dom.development.js:149)
at Object.invokeGuardedCallbackDev (react-dom.development.js:199)
at invokeGuardedCallback (react-dom.development.js:256)
at replayUnitOfWork (react-dom.development.js:17113)
at renderRoot (react-dom.development.js:17957)
at performWorkOnRoot (react-dom.development.js:18808)
at performWork (react-dom.development.js:18716)
at flushInteractiveUpdates$1 (react-dom.development.js:18987)
at batchedUpdates (react-dom.development.js:2210)
at dispatchEvent (react-dom.development.js:4946)
at interactiveUpdates$1 (react-dom.development.js:18974)
at interactiveUpdates (react-dom.development.js:2217)
at dispatchInteractiveEvent (react-dom.development.js:4923)
Exempel på kod nedan:
const {useState} = React;
function App() {
const [name, setName] = useState('Mary');
const [age, setAge] = useState(16);
if (age < 16) {
return (
<div>
Name:{' '}
<input
value={name}
onChange={e => {
setName(e.target.value);
}}
/>
<br />
Age:{' '}
<input
value={age}
type="number"
onChange={e => {
setAge(+e.target.value);
}}
/>
</div>
);
}
const [license, setLicense] = useState('A123456');
return (
<div>
Name:{' '}
<input
value={name}
onChange={e => {
setName(e.target.value);
}}
/>
<br />
Age:{' '}
<input
value={age}
type="number"
onChange={e => {
setAge(+e.target.value);
}}
/>
<br />
Driver License:{' '}
<input
value={license}
onChange={e => {
setLicense(e.target.value);
}}
/>
</div>
);
}
ReactDOM.render(<App />, document.querySelector('#app'));<script src="https://unpkg.com/[email /umd/react.development.js"></script>
<script src="https://unpkg.com/[email /umd/react-dom.development.js"></script>
<div id="app"></div>Svar #1
Problemet är att vid den första renderingen anropades 3 useState-hookar - name, age och license men efter att åldern ändras till ett värde under 16, anropas inte useState för license längre, vilket resulterar i att endast de första 2 hookarna anropas. Som React-dokumentationen säger:
Anropa inte Hooks inuti loopar, villkor eller nästlade funktioner. Använd istället alltid Hooks på den översta nivån av din React-funktion. Genom att följa denna regel säkerställer du att Hooks anropas i samma ordning varje gång en komponent renderas. Det är detta som gör att React korrekt kan bevara tillståndet för Hooks mellan flera
useStateochuseEffect-anrop.
Ordningen på de hooks som kallas är viktig, och om vi skriver kod som gör att hooks inte kallas, kommer React inte att kunna matcha hook-anropet med dess värden.
Lösningen är att flytta license-hooken upp till toppen av funktionen så att den kallas oavsett om den behövs eller inte.
const {useState} = React;
function App() {
const [name, setName] = useState('Mary');
const [age, setAge] = useState(16);
const [license, setLicense] = useState('A123456');
return (
<div>
Name:{' '}
<input
value={name}
onChange={e => {
setName(e.target.value);
}}
/>
<br />
Age:{' '}
<input
value={age}
type="number"
onChange={e => {
setAge(+e.target.value);
}}
/>
{age >= 16 && <span>
<br />
Driver License:{' '}
<input
value={license}
onChange={e => {
setLicense(e.target.value);
}}
/></span>
}
</div>
);
}
ReactDOM.render(<App />, document.querySelector('#app'));<script src="https://unpkg.com/[email /umd/react.development.js"></script>
<script src="https://unpkg.com/[email /umd/react-dom.development.js"></script>
<div id="app"></div>Svar #2
Jag har löst detta problem genom att ändra ordningen på React Hooks. useEffect kom efter ett tidigt return.
Så jag har ändrat koden från
function (){
const [loading ,setLoading] = React.useState(false);
if (loading) {
return "loading ....."
}
useEffect(() => {
if (condition) {
doSomething();
}
}, []);
return <div>component</div>
}
till
function (){
const [loading ,setLoading] = React.useState(false);
useEffect(() => {
if (condition) {
doSomething();
}
}, []);
if (loading){
return "loading ....."
}
return <div>component</div>
}
Så, när loading blir sant kommer useEffect inte att köras och det kommer att ge ett fel. Men om jag deklarerar loading JSX-elementet efter useEffect så kommer det att fungera.
Svar #3
Se till att du inte körde useEffect villkorligt.
Till exempel, om du har någon kod som följande:
if(condition) {
useEffect(()=>{
doSomething();
}, []);
}
Ändra det sedan till
useEffect(()=>{
if(condition) {
doSomething();
}
}, []);
Då skulle felet inte inträffa.
