在子组件中传递和调用的函数无权访问父状态值
Function passed and invoked in child component doesn't have access to parent state value
我有一个父组件和一个子组件。
父组件有我想要访问的状态变量。
我将一个函数传递给子组件并在那里调用它 onClick。
但是,状态变量的值已过时 - 不是当前值。
function Parent(props) {
const [items, setItems] = useState([]);
function getItems () {
console.log(items); // always prints "[]"
}
useEffect(() => {
thirdPartyApiCall().then((fetchedItems) =>
let newItems = fetchedItems.map(item => <Child id={item.id} getItems={getItems}/>)
setItems(newItems); // populates items array. "{items}" elements correctly displayed on web page
);
}, []);
}
function Child(props) {
return <button onClick={props.getItems}>{props.id}</button>
}
您可能想看看 javascript closure 的工作原理。
将给出正在发生的事情的基本概念。
由于依赖关系,useEffect
只会 运行 一次。所以当子组件被渲染时 items
是一个空数组([]
)。您的子组件将始终具有 items
的这个实例,因为 useEffect
没有被更新的 items
.
调用
希望对您有所帮助。
编辑:1
您可能希望您的父组件看起来像这样。
function Parent(props) {
const [items, setItems] = useState([]);
function getItems () {
console.log(items); // always prints "[]"
}
useEffect(() => {
thirdPartyApiCall().then((fetchedItems) =>
setItems(newItems);
);
}, []);
return newItems.map(item => <Child key={item.id} id={item.id} getItems={getItems}/>)
}
嗯,是的,点击按钮当然会记录 []
。在这:
<Child id={item.id} getItems={getItems}/>
getItem
被评估为记录项目的函数,并且项目是 []
,因此记录了 []
。它不记录 new 项目的原因是,在编写代码时,React 无法真正知道何时更新组件,因为在渲染阶段没有使用变量.
在 Render 之外使用 JSX 元素通常是一种不好的做法,因为它们并不是真正具有可预测行为的普通 JS 变量。它编译的事实更像是 hack 而不是 feature.
在状态中保留香草变量,让组件进行 JSX 渲染。此方法与您的方法 略有 不同,但按预期工作,因为单击任一按钮会记录所有项目:
const fakeApiCall = () => Promise.resolve([
{id: 10},
{id: 20},
{id: 30},
]);
const Parent = () => {
const [items, setItems] = React.useState([]);
React.useEffect(() => {
fakeApiCall().then(setItems)
})
const getItems = () => console.log(items);
return (
<div>
{items.map(item => (
<Child
id={item.id}
key={item.id}
getItems={getItems}/>
))}
</div>
)
}
const Child = ({id, getItems}) => (<button onClick={getItems}>{id}</button>);
window.onload = () => ReactDOM.render(<Parent/>, document.querySelector('#root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
我有一个父组件和一个子组件。
父组件有我想要访问的状态变量。
我将一个函数传递给子组件并在那里调用它 onClick。
但是,状态变量的值已过时 - 不是当前值。
function Parent(props) {
const [items, setItems] = useState([]);
function getItems () {
console.log(items); // always prints "[]"
}
useEffect(() => {
thirdPartyApiCall().then((fetchedItems) =>
let newItems = fetchedItems.map(item => <Child id={item.id} getItems={getItems}/>)
setItems(newItems); // populates items array. "{items}" elements correctly displayed on web page
);
}, []);
}
function Child(props) {
return <button onClick={props.getItems}>{props.id}</button>
}
您可能想看看 javascript closure 的工作原理。
将给出正在发生的事情的基本概念。
由于依赖关系,useEffect
只会 运行 一次。所以当子组件被渲染时 items
是一个空数组([]
)。您的子组件将始终具有 items
的这个实例,因为 useEffect
没有被更新的 items
.
希望对您有所帮助。
编辑:1
您可能希望您的父组件看起来像这样。
function Parent(props) {
const [items, setItems] = useState([]);
function getItems () {
console.log(items); // always prints "[]"
}
useEffect(() => {
thirdPartyApiCall().then((fetchedItems) =>
setItems(newItems);
);
}, []);
return newItems.map(item => <Child key={item.id} id={item.id} getItems={getItems}/>)
}
嗯,是的,点击按钮当然会记录 []
。在这:
<Child id={item.id} getItems={getItems}/>
getItem
被评估为记录项目的函数,并且项目是 []
,因此记录了 []
。它不记录 new 项目的原因是,在编写代码时,React 无法真正知道何时更新组件,因为在渲染阶段没有使用变量.
在 Render 之外使用 JSX 元素通常是一种不好的做法,因为它们并不是真正具有可预测行为的普通 JS 变量。它编译的事实更像是 hack 而不是 feature.
在状态中保留香草变量,让组件进行 JSX 渲染。此方法与您的方法 略有 不同,但按预期工作,因为单击任一按钮会记录所有项目:
const fakeApiCall = () => Promise.resolve([
{id: 10},
{id: 20},
{id: 30},
]);
const Parent = () => {
const [items, setItems] = React.useState([]);
React.useEffect(() => {
fakeApiCall().then(setItems)
})
const getItems = () => console.log(items);
return (
<div>
{items.map(item => (
<Child
id={item.id}
key={item.id}
getItems={getItems}/>
))}
</div>
)
}
const Child = ({id, getItems}) => (<button onClick={getItems}>{id}</button>);
window.onload = () => ReactDOM.render(<Parent/>, document.querySelector('#root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>