Tig*_*ran 4 javascript reactjs use-effect use-state
我正在使用 Redux 订阅商店并更新组件。
这是一个没有 Redux 的简化示例。它使用一个模型商店来订阅和分派。
请按照代码段下面的步骤重现问题。
编辑:请跳到更新下的第二个演示片段,以获得更简洁、更接近现实生活的场景。问题不在于 Redux。这是关于 React 的 setState 函数标识在某些情况下导致重新渲染,即使状态没有改变。
编辑 2:在“更新 2 ”下添加了更简洁的演示。
const {useState, useEffect} = React;
let counter = 0;
const createStore = () => {
const listeners = [];
const subscribe = (fn) => {
listeners.push(fn);
return () => {
listeners.splice(listeners.indexOf(fn), 1);
};
}
const dispatch = () => {
listeners.forEach(fn => fn());
};
return {dispatch, subscribe};
};
const store = createStore();
function Test() {
const [yes, setYes] = useState('yes');
useEffect(() => {
return store.subscribe(() => {
setYes('yes');
});
}, []);
console.log(`Rendered ${++counter}`);
return (
<div>
<h1>{yes}</h1>
<button onClick={() => {
setYes(yes === 'yes' ? 'no' : 'yes');
}}>Toggle</button>
<button onClick={() => {
store.dispatch();
}}>Set to Yes</button>
</div>
);
}
ReactDOM.render(<Test />, document.getElementById('root'));Run Code Online (Sandbox Code Playgroud)
<div id="root"></div>
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>Run Code Online (Sandbox Code Playgroud)
yes已经是“是”,状态不变,因此组件不会重新渲染。yes设置为“否”。状态已经改变,所以组件被重新渲染。yes设置为“是”。状态再次改变,所以组件被重新渲染。在第 4 步中,不应重新渲染组件,因为状态未更改。
正如React 文档所述,useEffect是
适用于许多常见的副作用,例如设置订阅和事件处理程序...
一个这样的用例可能是侦听浏览器事件,例如online和offline。
在这个例子中useEffect,当组件第一次渲染时,我们通过向它传递一个空数组来调用内部函数一次[]。该函数为在线状态更改设置事件侦听器。
假设,在应用程序的界面中,我们还有一个按钮可以手动切换在线状态。
请按照代码段下面的步骤重现问题。
const {useState, useEffect} = React;
let counter = 0;
function Test() {
const [online, setOnline] = useState(true);
useEffect(() => {
const onOnline = () => {
setOnline(true);
};
const onOffline = () => {
setOnline(false);
};
window.addEventListener('online', onOnline);
window.addEventListener('offline', onOffline);
return () => {
window.removeEventListener('online', onOnline);
window.removeEventListener('offline', onOffline);
}
}, []);
console.log(`Rendered ${++counter}`);
return (
<div>
<h1>{online ? 'Online' : 'Offline'}</h1>
<button onClick={() => {
setOnline(!online);
}}>Toggle</button>
</div>
);
}
ReactDOM.render(<Test />, document.getElementById('root'));Run Code Online (Sandbox Code Playgroud)
<div id="root"></div>
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>Run Code Online (Sandbox Code Playgroud)
online设置为false。状态已经改变,所以组件被重新渲染。online已经false,因此状态一直没有改变,但该组件是仍然重新呈现。在第 3 步中,不应重新渲染组件,因为状态未更改。
const {useState, useEffect} = React;
let counterRenderComplete = 0;
let counterRenderStart = 0;
function Test() {
const [yes, setYes] = useState('yes');
console.log(`Component function called ${++counterRenderComplete}`);
useEffect(() => console.log(`Render completed ${++counterRenderStart}`));
return (
<div>
<h1>{yes ? 'yes' : 'no'}</h1>
<button onClick={() => {
setYes(!yes);
}}>Toggle</button>
<button onClick={() => {
setYes('yes');
}}>Set to Yes</button>
</div>
);
}
ReactDOM.render(<Test />, document.getElementById('root'));
Run Code Online (Sandbox Code Playgroud)
<div id="root"></div>
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>Run Code Online (Sandbox Code Playgroud)
yes已经true,状态不变,因此组件不会重新渲染。yes设置为false。状态已经改变,所以组件被重新渲染。yes设置为true。状态再次改变,所以组件被重新渲染。为什么组件仍然重新渲染?我做错了什么,还是这是一个 React 错误?
答案是 React 使用一组启发式方法来确定它是否可以避免再次调用渲染函数。这些启发式方法可能会在不同版本之间发生变化,并且不能保证在状态相同时总是能够摆脱困境。React 提供的唯一保证是,如果状态相同,它不会重新渲染子组件。
你的渲染函数应该是纯的。因此,它们运行多少次并不重要。如果您在渲染函数中计算一些昂贵的东西并且担心不必要地调用它,您可以将该计算包装在useMemo.
一般来说,React 中的“计数渲染”是没有用的。React 何时准确调用你的函数取决于 React 本身,确切的时间会在版本之间不断变化。这不是合同的一部分。
| 归档时间: |
|
| 查看次数: |
1522 次 |
| 最近记录: |