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 时,这就会出现一个引用错误。

...