React:如果一个 useState 被更新了,它怎么可能同时不被更新呢?
React: If a useState is updated, how can it be also non-updated at the same time?
我有一个反应代码
- 设置一个
empty state
fills
州
- 一旦这个状态被填充,它
renders
一些图像
- 此图像随后触发
onLoad
事件
- 此 onLoad 事件然后调用一个
function
,reads
初始 state
- 但是这个状态是
empty
怎么可能?如果函数被调用,则表示状态不再为空
笔https://codesandbox.io/s/usestate-strange-things-9rydu
密码
import React, { useRef, useState, useEffect } from "react";
import styled from "@emotion/styled";
const useMyHook = (virtual_structure, setVirtual_structure) => {
useEffect(() => {
console.log("virtual_structure is updated!");
console.log(virtual_structure);
console.log("____virtual_structure is updated!");
}, [virtual_structure]);
const refs = useRef([]);
const createStructure = () => {
console.log("virtual_structure, is it empty?");
console.log(virtual_structure);
};
useEffect(() => {
createStructure();
}, []);
const assignRef = r =>
r && (refs.current.includes(r) || refs.current.push(r));
return [assignRef, createStructure];
};
export default function App() {
const [virtual_structure, setVirtual_structure] = useState([]);
const [assignRef, updateGrid] = useMyHook(
virtual_structure,
setVirtual_structure
);
useEffect(() => {
const temp_structure = Array.from({ length: 4 }, () => ({
height: 0,
cells: []
}));
temp_structure[0].cells = Array.from({ length: 10 }, () => {
const rand = Math.random();
const r = rand > 0.1 ? parseInt(500 * rand) : parseInt(500 * 0.1);
return {
height: "",
el: (
<div ref={assignRef}>
<Image
alt=""
onload={updateGrid}
num=""
src={`https://picsum.photos/200/${r}`}
/>
</div>
)
};
});
setVirtual_structure(temp_structure);
}, []);
return (
<Container>
{virtual_structure.map((col, i) => (
<div key={`col${i}`}>
{col.cells && col.cells.map((cell, j) => <>{cell.el}</>)}
</div>
))}
</Container>
);
}
const Image = ({ alt, onload, num, src }) => (
<>
<Label>{num}</Label>
<Img src={src} alt={alt} onLoad={onload} />
</>
);
const Img = styled.img`
border: 1px solid #000;
height: min-content;
margin: 0;
padding: 0;
`;
const Label = styled.div`
position: absolute;
`;
const Container = styled.div`
width: 100%;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
background: #ccc;
align-content: center;
div {
flex: 1;
div {
color: #fff;
font-weight: 700;
font-size: 32px;
margin: 4px;
}
}
`;
还有 console.log
virtual_structure is updated!
index.js:27 Array(0)length: 0__proto__: Array(0)
index.js:27 ____virtual_structure is updated!
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure is updated!
index.js:27 Array(4)0: {height: 0, cells: Array(10)}1: {height: 0, cells: Array(0)}2: {height: 0, cells: Array(0)}3: {height: 0, cells: Array(0)}length: 4__proto__: Array(0)
index.js:27 ____virtual_structure is updated!
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)length: 0__proto__: Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 []length: 0__proto__: Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 []
它的发生是由于 closures。
您将 updateGrid
函数传递给每个 Image
组件 一次挂载:
// useEffect closes its lexixal scope upon "updateGrid"
const [assignRef, updateGrid] = useMyHook(
virtual_structure,
setVirtual_structure
);
useEffect(() => {
...
temp_structure[0].cells = Array.from({ length: 10 }, () => {
return {
el: (
<div ref={assignRef}>
// v Used inside the callback scope
<Image onload={updateGrid} />
</div>
)
};
});
setVirtual_structure(temp_structure);
}, []);
但是,updateGrid
中 virtual_structure
的值(即您重命名的 createStructure
)实际上总是等于 []
在 useEffect
回调的词法范围内。尽管 createStructure
会在渲染时进行更新,但 它从未将 传递给 Image
组件 具有预期值 .
const createStructure = () => {
console.log('virtual_structure, is it empty?');
console.log(virtual_structure); // always virtual_structure=[]
};
Side note: never ignore lint warnings, although you might know what you are doing, it may lead to unexpected bugs.
正如在@Dennis-vash 的回答中所说,"closures" 冻结了作用域函数中的 useState
变量,因此该函数永远看不到当前(更新的)值这个变量
一个解决方法是,我总是可以调用一个更新状态的函数,而不是调用一个执行逻辑的函数,然后使用这个状态来触发该函数(现在是一个 useEffect 而不是一个函数)
如果有人想提出更好的替代方案来解决这个问题,我会把这个问题搁置几天 (?)
代码
https://codesandbox.io/s/usestate-strange-things-tneue
import React, { useRef, useState, useEffect } from "react";
import styled from "@emotion/styled";
const useMyHook = (virtual_structure, setVirtual_structure, updateGrid) => {
const refs = useRef([]);
useEffect(() => {
console.log("virtual_structure, is it empty?");
console.log(virtual_structure);
}, [updateGrid, virtual_structure]);
const assignRef = r =>
r && (refs.current.includes(r) || refs.current.push(r));
return [assignRef];
};
export default function App() {
const [virtual_structure, setVirtual_structure] = useState([]);
const [updateGrid, setUpdateGrid] = useState();
const [assignRef] = useMyHook(
virtual_structure,
setVirtual_structure,
updateGrid
);
const update = async () => setUpdateGrid(updateGrid + 1);
useEffect(() => {
const temp_structure = Array.from({ length: 4 }, () => ({
height: 0,
cells: []
}));
temp_structure[0].cells = Array.from({ length: 10 }, () => {
const rand = Math.random();
const r = rand > 0.1 ? parseInt(500 * rand) : parseInt(500 * 0.1);
return {
height: "",
el: (
<div ref={assignRef}>
<Image
alt=""
onload={update}
num=""
src={`https://picsum.photos/200/${r}`}
/>
</div>
)
};
});
setVirtual_structure(temp_structure);
}, []);
return (
<Container>
{virtual_structure.map((col, i) => (
<div key={`col${i}`}>
{col.cells &&
col.cells.map((cell, j) => (
<React.Fragment key={`cell${j}`}>{cell.el}</React.Fragment>
))}
</div>
))}
</Container>
);
}
const Image = ({ alt, onload, num, src }) => (
<>
<Label>{num}</Label>
<Img src={src} alt={alt} onLoad={onload} />
</>
);
const Img = styled.img`
border: 1px solid #000;
height: min-content;
margin: 0;
padding: 0;
`;
const Label = styled.div`
position: absolute;
`;
const Container = styled.div`
width: 100%;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
background: #ccc;
align-content: center;
div {
flex: 1;
div {
color: #fff;
font-weight: 700;
font-size: 32px;
margin: 4px;
}
}
`;
我有一个反应代码
- 设置一个
empty state
fills
州- 一旦这个状态被填充,它
renders
一些图像 - 此图像随后触发
onLoad
事件 - 此 onLoad 事件然后调用一个
function
,reads
初始state
- 但是这个状态是
empty
怎么可能?如果函数被调用,则表示状态不再为空
笔https://codesandbox.io/s/usestate-strange-things-9rydu
密码
import React, { useRef, useState, useEffect } from "react";
import styled from "@emotion/styled";
const useMyHook = (virtual_structure, setVirtual_structure) => {
useEffect(() => {
console.log("virtual_structure is updated!");
console.log(virtual_structure);
console.log("____virtual_structure is updated!");
}, [virtual_structure]);
const refs = useRef([]);
const createStructure = () => {
console.log("virtual_structure, is it empty?");
console.log(virtual_structure);
};
useEffect(() => {
createStructure();
}, []);
const assignRef = r =>
r && (refs.current.includes(r) || refs.current.push(r));
return [assignRef, createStructure];
};
export default function App() {
const [virtual_structure, setVirtual_structure] = useState([]);
const [assignRef, updateGrid] = useMyHook(
virtual_structure,
setVirtual_structure
);
useEffect(() => {
const temp_structure = Array.from({ length: 4 }, () => ({
height: 0,
cells: []
}));
temp_structure[0].cells = Array.from({ length: 10 }, () => {
const rand = Math.random();
const r = rand > 0.1 ? parseInt(500 * rand) : parseInt(500 * 0.1);
return {
height: "",
el: (
<div ref={assignRef}>
<Image
alt=""
onload={updateGrid}
num=""
src={`https://picsum.photos/200/${r}`}
/>
</div>
)
};
});
setVirtual_structure(temp_structure);
}, []);
return (
<Container>
{virtual_structure.map((col, i) => (
<div key={`col${i}`}>
{col.cells && col.cells.map((cell, j) => <>{cell.el}</>)}
</div>
))}
</Container>
);
}
const Image = ({ alt, onload, num, src }) => (
<>
<Label>{num}</Label>
<Img src={src} alt={alt} onLoad={onload} />
</>
);
const Img = styled.img`
border: 1px solid #000;
height: min-content;
margin: 0;
padding: 0;
`;
const Label = styled.div`
position: absolute;
`;
const Container = styled.div`
width: 100%;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
background: #ccc;
align-content: center;
div {
flex: 1;
div {
color: #fff;
font-weight: 700;
font-size: 32px;
margin: 4px;
}
}
`;
还有 console.log
virtual_structure is updated!
index.js:27 Array(0)length: 0__proto__: Array(0)
index.js:27 ____virtual_structure is updated!
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure is updated!
index.js:27 Array(4)0: {height: 0, cells: Array(10)}1: {height: 0, cells: Array(0)}2: {height: 0, cells: Array(0)}3: {height: 0, cells: Array(0)}length: 4__proto__: Array(0)
index.js:27 ____virtual_structure is updated!
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 Array(0)length: 0__proto__: Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 []length: 0__proto__: Array(0)
index.js:27 virtual_structure, is it empty?
index.js:27 []
它的发生是由于 closures。
您将 updateGrid
函数传递给每个 Image
组件 一次挂载:
// useEffect closes its lexixal scope upon "updateGrid"
const [assignRef, updateGrid] = useMyHook(
virtual_structure,
setVirtual_structure
);
useEffect(() => {
...
temp_structure[0].cells = Array.from({ length: 10 }, () => {
return {
el: (
<div ref={assignRef}>
// v Used inside the callback scope
<Image onload={updateGrid} />
</div>
)
};
});
setVirtual_structure(temp_structure);
}, []);
但是,updateGrid
中 virtual_structure
的值(即您重命名的 createStructure
)实际上总是等于 []
在 useEffect
回调的词法范围内。尽管 createStructure
会在渲染时进行更新,但 它从未将 传递给 Image
组件 具有预期值 .
const createStructure = () => {
console.log('virtual_structure, is it empty?');
console.log(virtual_structure); // always virtual_structure=[]
};
Side note: never ignore lint warnings, although you might know what you are doing, it may lead to unexpected bugs.
正如在@Dennis-vash 的回答中所说,"closures" 冻结了作用域函数中的 useState
变量,因此该函数永远看不到当前(更新的)值这个变量
一个解决方法是,我总是可以调用一个更新状态的函数,而不是调用一个执行逻辑的函数,然后使用这个状态来触发该函数(现在是一个 useEffect 而不是一个函数)
如果有人想提出更好的替代方案来解决这个问题,我会把这个问题搁置几天 (?)
代码
https://codesandbox.io/s/usestate-strange-things-tneue
import React, { useRef, useState, useEffect } from "react";
import styled from "@emotion/styled";
const useMyHook = (virtual_structure, setVirtual_structure, updateGrid) => {
const refs = useRef([]);
useEffect(() => {
console.log("virtual_structure, is it empty?");
console.log(virtual_structure);
}, [updateGrid, virtual_structure]);
const assignRef = r =>
r && (refs.current.includes(r) || refs.current.push(r));
return [assignRef];
};
export default function App() {
const [virtual_structure, setVirtual_structure] = useState([]);
const [updateGrid, setUpdateGrid] = useState();
const [assignRef] = useMyHook(
virtual_structure,
setVirtual_structure,
updateGrid
);
const update = async () => setUpdateGrid(updateGrid + 1);
useEffect(() => {
const temp_structure = Array.from({ length: 4 }, () => ({
height: 0,
cells: []
}));
temp_structure[0].cells = Array.from({ length: 10 }, () => {
const rand = Math.random();
const r = rand > 0.1 ? parseInt(500 * rand) : parseInt(500 * 0.1);
return {
height: "",
el: (
<div ref={assignRef}>
<Image
alt=""
onload={update}
num=""
src={`https://picsum.photos/200/${r}`}
/>
</div>
)
};
});
setVirtual_structure(temp_structure);
}, []);
return (
<Container>
{virtual_structure.map((col, i) => (
<div key={`col${i}`}>
{col.cells &&
col.cells.map((cell, j) => (
<React.Fragment key={`cell${j}`}>{cell.el}</React.Fragment>
))}
</div>
))}
</Container>
);
}
const Image = ({ alt, onload, num, src }) => (
<>
<Label>{num}</Label>
<Img src={src} alt={alt} onLoad={onload} />
</>
);
const Img = styled.img`
border: 1px solid #000;
height: min-content;
margin: 0;
padding: 0;
`;
const Label = styled.div`
position: absolute;
`;
const Container = styled.div`
width: 100%;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
background: #ccc;
align-content: center;
div {
flex: 1;
div {
color: #fff;
font-weight: 700;
font-size: 32px;
margin: 4px;
}
}
`;