简体   繁体   English

在子组件中传递和调用的函数无权访问父状态值

[英]Function passed and invoked in child component doesn't have access to parent state value

I have a parent and a child components. 我有一个父母和一个孩子的组成部分。

Parent component has state variable that I want to access. 父组件具有我要访问的状态变量。

I pass a function to child component and call it there onClick. 我将一个函数传递给子组件,然后在onClick上调用它。

However, the value of the state variable is out of date - not the current value. 但是,状态变量的值已过期-不是当前值。

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>
}

You might want to look at how javascript closure works. 您可能想看看javascript 闭包的工作方式。

Will give a basic idea of what's happening. 将给出正在发生的事情的基本概念。

useEffect would only run once, due to the dependency. 由于依赖, useEffect将只运行一次。 So when the child components were rendered items was an empty array( [] ). 因此,当渲染子组件时, items为空array( [] )。 Your child components would always have this instance of items as useEffect did not get called with updated items . 您的子组件将始终具有此items实例,因为useEffect不会被更新的items调用。

Hope this helped. 希望这会有所帮助。

EDIT:1 编辑:1

You might want your parent component to look like this. 您可能希望您的父组件看起来像这样。

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}/>)
}

Well, yes, of course clicking on the button logs [] . 好吧,是的,当然,单击按钮log [] In this: 在此:

<Child id={item.id} getItems={getItems}/>

getItem gets evaluated as a function that logs the items and the items are [] , so [] is logged. getItem被评估为记录项目的函数,并且项目为[] ,因此记录了[] The reason why it doesn't logs the new items is because, as your code is being written, React cannot really know when to update the component, because no variable is used in the render phase. 它不记录项目的原因是因为在编写代码时,React不能真正知道何时更新组件,因为在渲染阶段未使用任何变量。


This is generally a bad practice to use JSX elements outside of Render because they're not really normal JS variables with predictable behavior. 在Render之外使用JSX元素通常是一种不好的做法,因为它们并不是具有可预测行为的真正的普通JS变量。 The fact that it compiles is more a hack than a feature . 它编译的事实更多的是hack而不是功能

Keep vanilla variables in the state, and let the components do the JSX rendering. 将原始变量保持在该状态,并让组件执行JSX渲染。 This approach is slightly different than yours, but works as expected, as clicking on either button logs all the items: 这种方法与您的方法略有不同,但是可以正常工作,因为单击任一按钮都会记录所有项目:

 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> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM