React Strictmode 阻止自定义的 useState 挂钩在第一次调用时更改状态
React Strictmode preventing a custom made useState hook to change the state at the first call
我正在关注 Ryan florence(混音和 ReactTraining.com 的创作者)的 presentation。在那里,他通过自己制作一个钩子来消除 useState 钩子的雾气。
我遵循了这个过程,并在一个组件中实现了它。
import React from 'react'
import ReactDOM from 'react-dom';
function MadeUseStateMyself() {
const [minutes,setMinutes] = useState(5)
const [error,setError] = useState(null)
const handleAdd = () => {
if(minutes<9){
setMinutes(minutes+1)
setError(null)
}
else{
setError('Less than 10 please')
}
}
const handleSubStract = () => {
if(minutes>0){
setMinutes(minutes-1)
setError(null)
}
else{
setError('More than 0 please')
}
}
return (
<React.Fragment>
<button onClick={handleSubStract} > - </button>
<p>{minutes}</p>
<button onClick={handleAdd} > + </button>
{error && <div>{error}</div>}
</React.Fragment>
)
}
const states = []
let calls = -1
const useState = def => {
const callId = ++calls
if(states[callId]) {
return states[callId]
}
const setDef = newV => {
states[callId][0] = newV
reRender()
}
const state = [def,setDef]
states.push(state)
return state
}
function reRender(){
calls = -1
ReactDOM.render(<MadeUseStateMyself/>,document.getElementById('root'))
}
export default MadeUseStateMyself
在这里我注意到一个非常奇怪的行为,即每当我使用 React.StrictMode 包装我的整个组件时,状态在第一次函数调用后不会改变(在在这种情况下,我实现了一个递增按钮和一个递减按钮,它们改变了一个奇异整数状态的状态。)
如果我去掉 React.StrictMode 包装器,这个问题就不会出现。
这是一个CodeSandbox
阅读 StrictMode docs ,我的一个假设是,在严格模式下,函数组件主体被调用两次,它在第二次函数调用期间失去了它的第一个状态,因此失去了第一次状态变化。
我什至接近?
我看了大约 13 分钟的视频才发现您的代码与演示中的代码之间的第一个区别。演示者说要立即手动调用自定义 reRender
函数来进行初始渲染。从这里我注意到你渲染了你的应用程序两次。 MadeUseStateMyself
在自定义 reRender
函数中渲染一次,然后导出并再次渲染到 index.js
文件中的 DOM。
您实际上已经将 MadeUseStateMyself
组件的两个实例呈现为相互踩踏的 React。
如果你拿走你的代码并立即调用 reRender
,并且还完全删除 index.js
渲染代码,同时 还 包装 MadeUseStateMyself
在 React.StrictMode
组件中,您会看到它的渲染和更新没有问题。
MadeUseStateMyself
import React from "react";
import ReactDOM from "react-dom";
const states = [];
let calls = -1;
const useState = (def) => {
const callId = ++calls;
if (states[callId]) {
return states[callId];
}
const setDef = (newV) => {
states[callId][0] = newV;
reRender();
};
const state = [def, setDef];
states.push(state);
return state;
};
function MadeUseStateMyself() {
const [minutes, setMinutes] = useState(5);
const [error, setError] = useState(null);
const handleAdd = () => {
if (minutes < 9) {
setMinutes(minutes + 1);
setError(null);
} else {
setError("Less than 10 please");
}
};
const handleSubStract = () => {
if (minutes > 0) {
setMinutes(minutes - 1);
setError(null);
} else {
setError("More than 0 please");
}
};
return (
<React.Fragment>
<button onClick={handleSubStract}> - </button>
<p>{minutes}</p>
<button onClick={handleAdd}> + </button>
{error && <div>{error}</div>}
</React.Fragment>
);
}
function reRender() {
calls = -1;
ReactDOM.render(
<React.StrictMode> // <-- add the React.StrictMode
<MadeUseStateMyself />
</React.StrictMode>,
document.getElementById("root")
);
}
reRender(); // <-- Invoke immediately
我正在关注 Ryan florence(混音和 ReactTraining.com 的创作者)的 presentation。在那里,他通过自己制作一个钩子来消除 useState 钩子的雾气。
我遵循了这个过程,并在一个组件中实现了它。
import React from 'react'
import ReactDOM from 'react-dom';
function MadeUseStateMyself() {
const [minutes,setMinutes] = useState(5)
const [error,setError] = useState(null)
const handleAdd = () => {
if(minutes<9){
setMinutes(minutes+1)
setError(null)
}
else{
setError('Less than 10 please')
}
}
const handleSubStract = () => {
if(minutes>0){
setMinutes(minutes-1)
setError(null)
}
else{
setError('More than 0 please')
}
}
return (
<React.Fragment>
<button onClick={handleSubStract} > - </button>
<p>{minutes}</p>
<button onClick={handleAdd} > + </button>
{error && <div>{error}</div>}
</React.Fragment>
)
}
const states = []
let calls = -1
const useState = def => {
const callId = ++calls
if(states[callId]) {
return states[callId]
}
const setDef = newV => {
states[callId][0] = newV
reRender()
}
const state = [def,setDef]
states.push(state)
return state
}
function reRender(){
calls = -1
ReactDOM.render(<MadeUseStateMyself/>,document.getElementById('root'))
}
export default MadeUseStateMyself
在这里我注意到一个非常奇怪的行为,即每当我使用 React.StrictMode 包装我的整个组件时,状态在第一次函数调用后不会改变(在在这种情况下,我实现了一个递增按钮和一个递减按钮,它们改变了一个奇异整数状态的状态。)
如果我去掉 React.StrictMode 包装器,这个问题就不会出现。
这是一个CodeSandbox
阅读 StrictMode docs ,我的一个假设是,在严格模式下,函数组件主体被调用两次,它在第二次函数调用期间失去了它的第一个状态,因此失去了第一次状态变化。
我什至接近?
我看了大约 13 分钟的视频才发现您的代码与演示中的代码之间的第一个区别。演示者说要立即手动调用自定义 reRender
函数来进行初始渲染。从这里我注意到你渲染了你的应用程序两次。 MadeUseStateMyself
在自定义 reRender
函数中渲染一次,然后导出并再次渲染到 index.js
文件中的 DOM。
您实际上已经将 MadeUseStateMyself
组件的两个实例呈现为相互踩踏的 React。
如果你拿走你的代码并立即调用 reRender
,并且还完全删除 index.js
渲染代码,同时 还 包装 MadeUseStateMyself
在 React.StrictMode
组件中,您会看到它的渲染和更新没有问题。
MadeUseStateMyself
import React from "react";
import ReactDOM from "react-dom";
const states = [];
let calls = -1;
const useState = (def) => {
const callId = ++calls;
if (states[callId]) {
return states[callId];
}
const setDef = (newV) => {
states[callId][0] = newV;
reRender();
};
const state = [def, setDef];
states.push(state);
return state;
};
function MadeUseStateMyself() {
const [minutes, setMinutes] = useState(5);
const [error, setError] = useState(null);
const handleAdd = () => {
if (minutes < 9) {
setMinutes(minutes + 1);
setError(null);
} else {
setError("Less than 10 please");
}
};
const handleSubStract = () => {
if (minutes > 0) {
setMinutes(minutes - 1);
setError(null);
} else {
setError("More than 0 please");
}
};
return (
<React.Fragment>
<button onClick={handleSubStract}> - </button>
<p>{minutes}</p>
<button onClick={handleAdd}> + </button>
{error && <div>{error}</div>}
</React.Fragment>
);
}
function reRender() {
calls = -1;
ReactDOM.render(
<React.StrictMode> // <-- add the React.StrictMode
<MadeUseStateMyself />
</React.StrictMode>,
document.getElementById("root")
);
}
reRender(); // <-- Invoke immediately