javascript - 在一个常量条件下,调用响应钩子是安全的?

142 5

hook的规则要求在每次呈现时都调用相同的钩子和相同的顺序。 如果你打破这个规则,就会出现什么问题。 例如这里代码:


function App() {


 console.log('render');


 const [flag, setFlag] = useState(true);


 const [first] = useState('first');


 console.log('first is', first);


 if (flag) {


 const [second] = useState('second');


 console.log('second is', second);


 }


 const [third] = useState('third');


 console.log('third is', third);



 useEffect(() => setFlag(false), []);



 return null;


}



输出到控制台


render 


first is first 


second is second 


third is third 


render 


first is first 


third is second 



并产生警告或者错误。

但是,在元素生命周期中没有改变的情况?


const DEBUG = true;



function TestConst() {


 if (DEBUG) {


 useEffect(() => console.log('rendered'));


 }



 return <span>test</span>;


}



这段代码并没有真正破坏规则,看起来很好。 但它仍然会触发eslint警告。

此外,似乎还可以基于道具编写类似代码:


function TestState({id, debug}) {


 const [isDebug] = useState(debug);



 if (isDebug) {


 useEffect(() => console.log('rendered', id));


 }



 return <span>{id}</span>;


}



function App() {


 const [counter, setCounter] = useState(0);


 useEffect(() => setCounter(1), []);


 return (


 <div>


 <TestState id="1" debug={false}/>


 <TestState id="2" debug={true}/>


 </div>


 );


}



这里代码按预期工作。

因此,当我确定它不会改变时,在一个条件内调用钩子是否安全? 是否可以修改eslint规则来识别这种情况?

问题更多的是真正的要求,而不是实现类似行为的方式。 据我所知,这对

确保每次组件呈现时都按相同的顺序调用钩子。 这就是在多个useState和useEffect调用之间正确地保持钩子状态的原因

这里有一个例外的地方: "不调用钩子 inside 循环,条件或者嵌套函数"。

时间:

66 2

虽然您可以像上面提到的那样有条件地编写钩子,但它可能目前正在工作,但它可能导致将来出现预期的行为。 例如在当前情况下,你不修改 isDebug 状态。

演示


const {useState, useEffect} = React;


function TestState({id, debug}) {


 const [isDebug, setDebug] = useState(debug);



 if (isDebug) {


 useEffect(() => console.log('rendered', id));


 }



 const toggleButton = () => {


 setDebug(prev =>!prev);


 }



 return (


 <div>


 <span>{id}</span>


 <button type="button" onClick={toggleButton}>Toggle debug</button>


 </div>


 );


}



function App() {


 const [counter, setCounter] = useState(0);


 useEffect(() => setCounter(1), []);


 return (


 <div>


 <TestState id="1" debug={false}/>


 <TestState id="2" debug={true}/>


 </div>


 );


}



ReactDOM.render(<App/>, document.getElementById('app'));

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>


<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>


<div id="app"/>

作为一个经验规则,你不应该违反规则,因为它可以能是未来问题的原因。 您可以通过以下方式处理上述方案而不违反规则


const {useState, useEffect} = React;


function TestState({id, debug}) {


 const [isDebug, setDebug] = useState(debug);



 useEffect(() => {


 if(isDebug) {


 console.log('rendered', id)


 }


 }, [isDebug]);



 const toggleButton = () => {


 setDebug(prev =>!prev);


 }



 return (


 <div>


 <span>{id}</span>


 <button type="button" onClick={toggleButton}>Toggle debug</button>


 </div>


 );


}



function App() {


 const [counter, setCounter] = useState(0);


 useEffect(() => setCounter(1), []);


 return (


 <div>


 <TestState id="1" debug={false}/>


 <TestState id="2" debug={true}/>


 </div>


 );


}



ReactDOM.render(<App/>, document.getElementById('app'));

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>


<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>


<div id="app"/>
原作者:
60 0

对于你的用例我没有看到问题,我不知道将来如何打破这个问题,你说它是正确的,它是按预期工作的。

但是,我认为警告实际上是合法的,并且应该始终存在,因为这可能是您的代码中的潜在错误(不是在这个特定的错误中)

所以我在你的例子中要做的就是禁用这个行的react-hooks/rules-of-hooks 规则。

参考:https://reactjs.org/docs/hooks-rules.html

140 4

请不要使用这里 Pattern 。 它可能适用于你的例子,但它不是很好(或惯用)。

标准模式(有充分理由)是在构造函数中声明初始状态,然后更新以响应正文中的某些条件(setState)。 这种方法可以在无状态组件中反映这里功能,因此它应该。

其次,我看不出动态添加这段状态如何有用,并可能导致以后的渲染问题。 在你的例子中,一个简单的const也可以正常工作 - 没有理由使用动态状态。

设想一下:


return (<React.Fragment>{second}</React.Fragment>)



当你没有定义 second 时,这就会出现一个引用错误。

...