React Hooks FAQ:getderivedstatefromprops 的实现导致渲染状态不连贯
React Hooks FAQ: implementation of getderivedstatefromprops leads to rendering with incoherent state
恕我直言 来自 React Hooks FAQ #getDerivedStateFromProps 的建议
导致第一次呈现的值 row
与 isScrollingDown
的值不对应。由于调用 setIsScrollingDown
只会安排新的渲染,不会影响当前渲染,因此后者将使用新值 row
和旧值 isScrollingDown
执行。
此行为不等同于允许 row
和 isScrollingDown
之间连贯的组件 class 的静态 getderivedstatefromprops
方法。
是否应该使用类似以下代码的内容更新示例以保证连贯的渲染?还是我错过了什么?
谢谢!
function ScrollView({row}) {
let [isScrollingDown, setIsScrollingDown] = useState(false);
let [prevRow, setPrevRow] = useState(null);
if (row !== prevRow) {
// Row changed since last render. Update isScrollingDown.
isScrollingDown = prevRow !== null && row > prevRow
setIsScrollingDown(isScrollingDown);
setPrevRow(row);
}
return `Scrolling down: ${isScrollingDown}`;
}
文档中的重要部分无需您进行更改:
React will re-run the component with updated state immediately after
exiting the first render so it wouldn’t be expensive.
不同步的渲染将永远不会提交给浏览器。事实上,如果它从渲染返回一个子组件,子组件的渲染直到状态更新后才会执行(从更新状态的渲染返回的子组件将被忽略)。
下面是一个示例,其中添加了控制台日志来说明这一点。请注意,当您增加行时,ScrollView 会呈现两次,但 ScrollingDown 只会呈现一次,仅接收到 ScrollView 状态的最新版本。
import React, { useState } from "react";
import ReactDOM from "react-dom";
function ScrollingDown({ isScrollingDown, prevRow, row }) {
console.log("ScrollingDown", isScrollingDown, prevRow, row);
return (
<div>
{`Scrolling down: ${isScrollingDown}`}
<br />
{`prevRow: ${prevRow}`}
<br />
{`row: ${row}`}
</div>
);
}
function ScrollView({ row }) {
let [isScrollingDown, setIsScrollingDown] = useState(false);
let [prevRow, setPrevRow] = useState(null);
if (row !== prevRow) {
// Row changed since last render. Update isScrollingDown.
setIsScrollingDown(prevRow !== null && row > prevRow);
setPrevRow(row);
}
console.log("ScrollView", isScrollingDown, prevRow, row);
return (
<ScrollingDown
isScrollingDown={isScrollingDown}
prevRow={prevRow}
row={row}
/>
);
}
function App() {
const [row, setRow] = useState(1);
return (
<div className="App">
<ScrollView row={row} />
<button onClick={() => setRow(prev => prev + 1)}>Increment Row</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
恕我直言 来自 React Hooks FAQ #getDerivedStateFromProps 的建议
导致第一次呈现的值 row
与 isScrollingDown
的值不对应。由于调用 setIsScrollingDown
只会安排新的渲染,不会影响当前渲染,因此后者将使用新值 row
和旧值 isScrollingDown
执行。
此行为不等同于允许 row
和 isScrollingDown
之间连贯的组件 class 的静态 getderivedstatefromprops
方法。
是否应该使用类似以下代码的内容更新示例以保证连贯的渲染?还是我错过了什么?
谢谢!
function ScrollView({row}) {
let [isScrollingDown, setIsScrollingDown] = useState(false);
let [prevRow, setPrevRow] = useState(null);
if (row !== prevRow) {
// Row changed since last render. Update isScrollingDown.
isScrollingDown = prevRow !== null && row > prevRow
setIsScrollingDown(isScrollingDown);
setPrevRow(row);
}
return `Scrolling down: ${isScrollingDown}`;
}
文档中的重要部分无需您进行更改:
React will re-run the component with updated state immediately after exiting the first render so it wouldn’t be expensive.
不同步的渲染将永远不会提交给浏览器。事实上,如果它从渲染返回一个子组件,子组件的渲染直到状态更新后才会执行(从更新状态的渲染返回的子组件将被忽略)。
下面是一个示例,其中添加了控制台日志来说明这一点。请注意,当您增加行时,ScrollView 会呈现两次,但 ScrollingDown 只会呈现一次,仅接收到 ScrollView 状态的最新版本。
import React, { useState } from "react";
import ReactDOM from "react-dom";
function ScrollingDown({ isScrollingDown, prevRow, row }) {
console.log("ScrollingDown", isScrollingDown, prevRow, row);
return (
<div>
{`Scrolling down: ${isScrollingDown}`}
<br />
{`prevRow: ${prevRow}`}
<br />
{`row: ${row}`}
</div>
);
}
function ScrollView({ row }) {
let [isScrollingDown, setIsScrollingDown] = useState(false);
let [prevRow, setPrevRow] = useState(null);
if (row !== prevRow) {
// Row changed since last render. Update isScrollingDown.
setIsScrollingDown(prevRow !== null && row > prevRow);
setPrevRow(row);
}
console.log("ScrollView", isScrollingDown, prevRow, row);
return (
<ScrollingDown
isScrollingDown={isScrollingDown}
prevRow={prevRow}
row={row}
/>
);
}
function App() {
const [row, setRow] = useState(1);
return (
<div className="App">
<ScrollView row={row} />
<button onClick={() => setRow(prev => prev + 1)}>Increment Row</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);