未知的错误:渲染的钩子少于预期。这可能是由于React Hooks中的意外提早回报声明引起的
给定以下组件,当我按下年龄选择器并将值更改为 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/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<div id="app"></div>
问题在于,在第一次渲染中,调用了 3 个
useState
钩子 -
name
、
age
和
license
,但是在 age 更改为低于 16 的值后,
license
的
useState
不再被调用,导致只调用前 2 个钩子。正如
React 文档所述
:
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
anduseEffect
calls.
调用钩子的顺序很重要,如果我们编写的代码导致钩子不被调用,React 将无法将钩子调用与其值匹配。
解决方案是将
license
钩子移到函数的顶部,这样无论是否需要它,都会被调用。
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 protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<div id="app"></div>
我已通过更改 React Hooks 的顺序解决了此问题。
useEffect
在早期
return
之后出现。
因此,我已将代码从
function (){
const [loading ,setLoading] = React.useState(false);
if (loading) {
return "loading ....."
}
useEffect(() => {
if (condition) {
doSomething();
}
}, []);
return <div>component</div>
}
更改为
function (){
const [loading ,setLoading] = React.useState(false);
useEffect(() => {
if (condition) {
doSomething();
}
}, []);
if (loading){
return "loading ....."
}
return <div>component</div>
}
因此,当 loading 变为 true 时,
useEffect
将不会运行,并且会引发错误。但如果我在
useEffect
之后声明加载 JSX 元素,那么它就会起作用。
确保您没有有条件地运行
useEffect
。
例如,如果您有如下代码:
if(condition) {
useEffect(()=>{
doSomething();
}, []);
}
然后将其更改为
useEffect(()=>{
if(condition) {
doSomething();
}
}, []);
则不会发生错误。