未捕获错误: 呈现的钩子比预期的少。这可能是由于反应钩子中的意外提前返回语句引起的

考虑到以下组件,当我按下年龄选择器并将值更改为15,以便呈现一个没有驾照字段的表单时,会得到错误:

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)

下面的示例代码:

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/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>

91840 次浏览

The problem is that in the first render, 3 useState hooks were invoked - name, age and license but after the age is changed to a value below 16, the useState for license is no longer invoked, resulting in only the first 2 hooks being invoked. As the React docs state:

Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple useState and useEffect calls.

The order of the hooks being called is important, and if we write code that causes hooks to not be called, React will not be able to match the hook call with its values.

The solution is to move the license hook up to the top of the function so that it gets called regardless whether it is needed or not.

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/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>

Make sure that you didn't run useEffect conditionally.

For example, if you have some code like the following:

if(condition) {
useEffect(()=>{
doSomething();
}, []);
}

Then change it to

useEffect(()=>{
if(condition) {
doSomething();
}
}, []);

Then the error would not happen.

I have resolved this issue by changing the order of React Hooks.

So I have changed code from

function(){


const [loading ,setLoading] = React.useState(false);


if(loading){
return "loading ....."
}


useEffect(()=>{
if(condition) {
doSomething();
}
}, []);


return <div>component</div>
}

to

function(){


const [loading ,setLoading] = React.useState(false);




useEffect(()=>{
if(condition) {
doSomething();
}
}, []);




if(loading){
return "loading ....."
}
return <div>component</div>
}

So , when loading becomes true then useEffect will not run and that will throw error..but if i declare loading JSX element after useEffect then it will work perfect.

So I also experienced this error, but it was not related to the hook being wrapped in a condition. Instead it was caused by me having bad key values.

I had a component that moved items from one list to another by dragging and dropping. However, some of those items were locked, so I simply removed the reference to my hook in those elements.

The issue was that I used the index as a key, thus when I dragged a new element on top it got the same key as the locked element, the value of ref was changed and React complained with the above error.

So my answer to those who come across this issue is - check whether you have truly unique keys!

Today i have just met this error, i fixed it by change the position of hook: useEffect() from middle to header of function. And make sure do NOT use hook inside loop.

You can tell React to skip applying an effect if certain values haven’t changed between re-renders. To do so, pass an array as an optional second argument to useEffect:

      useEffect( () => {
dispatch({type: 'GET_PAGE', slug: Param});
setPagedata( data );
});

TO


useEffect( () => {
dispatch({type: 'GET_PAGE', slug: Param});
}, [Param]);


useEffect( () => {
setPagedata( data );
}, [data]);

ANd it works!

I have faced the above error in my own react application, when I used the useCallback hook inside the render component (and crucially, within an if condition). Moving the hook outside of functional component resolved the error. The lesson: don't use hooks inside conditional statements.

If you conditionally render, do not put component property data in between!

if (splashScreen)
return <Spinner>Loading</Spinner>


//DO NOT PUT THEM HERE


return (
...
)

This can also happen if you have hot reload (HMR) turned on and you're adding or deleting a useEffect. Just reload the page and the error will not be reproduced.