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

时间:2019-03-10 17:09:08

标签: javascript reactjs react-hooks

我有一个父项和一个子项。

父组件具有我要访问的状态变量。

我将一个函数传递给子组件,然后在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>
}

2 个答案:

答案 0 :(得分:2)

您可能想看看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}/>)
}

答案 1 :(得分:2)

是的,当然,单击按钮日志[]。在此:

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

getItem被评估为记录项目的函数,并且项目为[],因此记录了[]。它不记录 new 项的原因是因为在编写代码时,React无法真正知道何时更新组件,因为在渲染阶段未使用任何变量。


在Render之外使用JSX元素通常是一个坏习惯,因为它们不是具有可预测行为的真正的普通JS变量。它编译的事实更多地是 hack 而不是 features

将普通变量保持在该状态,并让组件执行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>