Dev*_*per 11 javascript reactjs react-strictmode
考虑下面的片段。在 React 18 中,count每次渲染都会在控制台上打印两次,但在 React 17 中只打印一次。
反应 18 示例:
function App() {
const [count, setCount] = React.useState(0);
console.log(count);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);Run Code Online (Sandbox Code Playgroud)
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="root"></div>Run Code Online (Sandbox Code Playgroud)
反应 17 示例
function App() {
const [count, setCount] = React.useState(0);
console.log(count);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);Run Code Online (Sandbox Code Playgroud)
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="root"></div>Run Code Online (Sandbox Code Playgroud)
我知道这与什么有关,StrictMode但我不确定是什么。而且我一直不清楚严格模式是如何工作的以及它的目的是什么,所以如果有人也能强调这一点,我将不胜感激。
Som*_*jee 33
长话短说
\n当组件被包装在 中时StrictMode,React 会运行某些函数两次,以帮助开发人员捕获代码中的错误。
这种情况在 React 18 和 React 17 中都会发生,但您在后者中没有遇到这种情况的原因是因为在 React 17 中,React 自动在第二次调用中静默日志。
\n如果您提取console.log并使用提取的别名进行记录,那么两个版本都会得到类似的行为。
const log = console.log;\n\nfunction App() {\n const [count, setCount] = React.useState(0);\n log(count);\n return <button onClick={() => setCount(count + 1)}>{count}</button>;\n}\n\nReactDOM.render(\n <React.StrictMode>\n <App />\n </React.StrictMode>,\n document.getElementById("root")\n);Run Code Online (Sandbox Code Playgroud)\r\n<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>\n<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>\n<div id="root"></div>Run Code Online (Sandbox Code Playgroud)\r\n\n\n注意:\n
\n
\n在 React 17 中,React 会自动修改控制台方法,例如console.log()在第二次调用生命周期函数时静默日志。但是,在某些可以使用解决方法的情况下,它可能会导致不良行为。\n
\n从 React 18 开始,React 不会抑制任何日志。但是,如果您安装了 React DevTools,第二次调用的日志将稍微变暗。React DevTools 还提供了一个设置(默认情况下关闭)来完全抑制它们。\n
\n源代码
现在让我们深入了解严格模式下实际发生的情况以及它有何帮助。
\n严格模式是一个工具,可帮助识别在使用 React 时可能导致问题的编码模式,例如不纯渲染。
\n在开发的严格模式下,React 会运行以下函数两次:
\n这是因为您的组件、初始化程序和更新程序需要是纯函数,但如果它们不是\xe2\x80\x99t,那么双重调用它们可能有助于发现此错误。如果它们是纯粹的,那么代码中的逻辑就不会受到任何影响。
\n注意: React 仅使用其中一个调用的结果,并忽略另一个调用的结果。
\n在下面的示例中,观察到组件、初始化程序和更新程序在开发过程中包装时都运行两次StrictMode(代码片段使用 React 的开发版本)。
// Extracting console.log in a variable because we\'re using React 17\nconst log = console.log; \n\nfunction App() {\n const [count, setCount] = React.useState(() => {\n log("Initializers run twice");\n return 0;\n });\n\n log("Components run twice");\n\n const handleClick = () => {\n log("Event handlers don\xe2\x80\x99t need to be pure, so they run only once");\n setCount((count) => {\n log("Updaters run twice");\n return count + 1;\n });\n };\n\n return (\n <div>\n <p>Count: {count}</p>\n <button onClick={handleClick}>Increment</button>\n </div>\n );\n}\n\nReactDOM.render(\n <React.StrictMode>\n <App />\n </React.StrictMode>,\n document.getElementById("root")\n);Run Code Online (Sandbox Code Playgroud)\r\n<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>\n<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>\n<div id="root"></div>Run Code Online (Sandbox Code Playgroud)\r\n上面例子的一些注释:
\n您可能已经注意到,当您第一次单击该按钮时,Updaters run twice日志仅打印一次,但在后续单击时,它会打印两次。但是您可以忽略此行为并假设它总是打印两次,但如果您想了解更多详细信息,您可以关注此github 问题。
我们必须提取console.log到一个单独的变量中才能获取打印的两个调用的日志,这是因为 React 17 自动静默第二次调用的日志(如 TL;DR 中所述)。如果您将 CDN 链接更新到 React 18,则不需要进行此提取。
调用setCount更新程序函数两次并不意味着它现在count每次点击都会增加两次,不,因为它两次调用具有相同状态的更新程序。因此,只要您的更新程序是纯函数,您的应用程序就不会受到 no 的影响。它\xe2\x80\x99 调用的次数。
“更新程序”和“初始化程序”是 React 中的通用术语。状态更新器和状态初始化器只是其中之一。其他更新程序是传递给“回调”useMemo和“缩减程序”。另一个初始化器是useReducer初始化器等。所有这些都应该是纯函数,因此严格模式双精度调用所有这些。看看这个例子:
const logger = console.log;\n\nconst countReducer = (count, incrementor) => {\n logger("Updaters [reducers] run twice");\n return count + incrementor;\n};\n\nfunction App() {\n const [count, incrementCount] = React.useReducer(\n countReducer,\n 0,\n (initCount) => {\n logger("Initializers run twice");\n return initCount;\n }\n );\n\n const doubleCount = React.useMemo(() => {\n logger("Updaters [useMemo callbacks] run twice");\n return count * 2;\n }, [count]);\n\n return (\n <div>\n <p>Double count: {doubleCount}</p>\n <button onClick={() => incrementCount(1)}>Increment</button>\n </div>\n );\n}\n\nconst root = ReactDOM.createRoot(document.getElementById("root"));\nroot.render(\n <React.StrictMode>\n <App />\n </React.StrictMode>\n);Run Code Online (Sandbox Code Playgroud)\r\n<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>\n<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>\n<div id="root"></div>Run Code Online (Sandbox Code Playgroud)\r\n让我们看一个示例,其中严格模式可以帮助我们发现严重错误。
\n// This example is in React 18 to highlight the fact that \n// the double invocation behavior is similar in both React 17 & 18.\n\nfunction App() {\n const [todos, setTodos] = React.useState([\n { id: 1, text: "Learn JavaScript", isComplete: true },\n { id: 2, text: "Learn React", isComplete: false }\n ]);\n\n const handleTodoCompletion = (todoId) => {\n setTodos((todos) => {\n console.log(JSON.stringify(todos));\n return todos.map((todo) => {\n if (todo.id === todoId) {\n todo.isComplete = !todo.isComplete; // Mutation here\n }\n return todo;\n });\n });\n };\n\n return (\n <ul>\n {todos.map((todo) => (\n <li key={todo.id}>\n <span\n style={{\n textDecoration: todo.isComplete ? "line-through" : "none"\n }}\n >\n {todo.text}\n </span>\n <button onClick={() => handleTodoCompletion(todo.id)}>\n Mark {todo.isComplete ? "Incomplete" : "Complete"}\n </button>\n </li>\n ))}\n </ul>\n );\n}\n\nconst root = ReactDOM.createRoot(document.getElementById("root"));\nroot.render(<App />);Run Code Online (Sandbox Code Playgroud)\r\n<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>\n<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>\n<div id="root"></div>Run Code Online (Sandbox Code Playgroud)\r\n上面的例子有什么问题呢?
\n您可能会注意到这些按钮无法按预期工作,它们不会切换布尔isComplete值,问题是传递给的更新器函数setTodos不是纯函数,因为它会改变状态中的对象todos。由于更新程序被调用两次,并且它不是纯函数,因此第二次调用将isComplete布尔值反转回它\xe2\x80\x99s 的原始值。
注意:只是因为严格模式的双重调用,我们才能够捕获这个错误。如果我们选择退出严格模式,那么该组件将幸运地按预期工作,但这并不意味着代码编写正确,它只能工作,因为组件是如何隔离的,在现实世界中,像这样的突变可能会导致严重的后果问题。即使您幸运地摆脱了此类突变,您仍然可能会遇到问题,因为目前更新程序依赖于每次点击仅调用一次的事实,但这不是React所保证的(考虑到并发功能) 。
\n如果您将更新程序设置为纯函数,则可以解决该问题:
\nsetTodos((todos) => {\n logger(JSON.stringify(todos, null, 2));\n return todos.map((todo) =>\n todo.id === todoId ? { ...todo, isComplete: !todo.isComplete } : todo\n );\n});\nRun Code Online (Sandbox Code Playgroud)\n在 React 18 中,\xc2\xa0 StrictMode\xc2\xa0 获得额外的行为以确保其与可重用状态兼容。当启用严格模式时,\xc2\xa0 React 会故意为新安装的组件双重调用效果 (mount\xc2\xa0->\xc2\xa0unmount\xc2\xa0->\xc2\xa0mount)。这是为了确保组件能够多次“安装”和“卸载”。与其他严格模式行为一样,React 仅针对开发构建执行此操作。
考虑下面的例子(来源):
\nsetTodos((todos) => {\n logger(JSON.stringify(todos, null, 2));\n return todos.map((todo) =>\n todo.id === todoId ? { ...todo, isComplete: !todo.isComplete } : todo\n );\n});\nRun Code Online (Sandbox Code Playgroud)\r\nfunction App(props) {\n React.useEffect(() => {\n console.log("Effect setup code runs");\n\n return () => {\n console.log("Effect cleanup code runs");\n };\n }, []);\n\n React.useLayoutEffect(() => {\n console.log("Layout effect setup code runs");\n\n return () => {\n console.log("Layout effect cleanup code runs");\n };\n }, []);\n \n console.log("React renders the component")\n \n return <h1>Strict Effects In React 18</h1>;\n}\n\nconst root = ReactDOM.createRoot(document.getElementById("root"));\nroot.render(\n <React.StrictMode>\n <App />\n </React.StrictMode>\n);Run Code Online (Sandbox Code Playgroud)\r\n上面的组件App声明了一些要在安装和卸载时运行的效果。在 React 18 之前,设置函数只会运行一次(在组件最初安装之后),清理函数也只会运行一次(在组件卸载之后)。但在 React 18 中StrictMode,会发生以下情况:
| 归档时间: |
|
| 查看次数: |
9054 次 |
| 最近记录: |