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

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>