繁体   English   中英

反应:在 useCallback 中获取和更新 state 的问题

[英]React: Issue with fetching and updating the state in useCallback

我目前正在开发一个组件,该组件进行 API 调用,检索数据,然后在 Fluent UI Datalist 中显示数据。

问题如下:组件第一次加载,然后在 API 调用后重新渲染,组件在表中显示正确的条目,其中 state.items 设置为正确的值。 但是,当我单击列运行 onColumnClick 时,function 内的项目为空,从而导致错误。 列很好,但 state.items 只是一个空集合。

如何解决这个问题,以便我看到 onColumnClick 中的项目?

这是一段代码:

export const ListComponent = (props: ListComponentProps) => {

    const fetchPeople = async () => {
        const entry: ITableEntry[] = [];

        //items ... sdk call

        for await (const item of items) {
            entry.push({
                key: item.id,
                name: item.name,
                lastName: item.lastname
            });
        }
    }

    useEffect(() => {
        fetchPeople();
        .then(elementList => {
            setState(
                state => ({ ...state, items: elementList }),
            );
        });
    }, [])

    const onColumnClick = React.useCallback((ev: React.MouseEvent<HTMLElement>, column: IColumn): void => {
        
        const columns = state.columns;
        const items = state.items;
        // PLACE WHERE THE ERROR HAPPENS
        console.log(items);
    }, []);


    const columns: IColumn[] = [
        {
            key: 'column1',
            name: 'First Name',
            fieldName: 'name',
            minWidth: 210,
            maxWidth: 350,
            isRowHeader: true,
            isResizable: true,
            isSorted: true,
            isSortedDescending: false,
            sortAscendingAriaLabel: 'Sorted A to Z',
            sortDescendingAriaLabel: 'Sorted Z to A',
            onColumnClick: onColumnClick,
            data: 'string',
            isPadded: true,
        },
        {
            key: 'column2',
            name: 'Last Name',
            fieldName: 'lastname',
            minWidth: 210,
            maxWidth: 350,
            isRowHeader: true,
            isResizable: true,
            isSorted: true,
            isSortedDescending: false,
            sortAscendingAriaLabel: 'Sorted A to Z',
            sortDescendingAriaLabel: 'Sorted Z to A',
            onColumnClick: onColumnClick,
            data: 'string',
            isPadded: true,
        },
    ];

    const [state, setState] = React.useState({
        items: [] as ITableEntry[],
        columns: columns,
    });

    return (
        <>
            <DetailsList
                items={state.items}
                columns={state.columns}
            />
        </>
    );
});
const onColumnClick = React.useCallback((ev: React.MouseEvent<HTMLElement>, column: IColumn): void => {   
     const columns = state.columns;
     const items = state.items;
     // PLACE WHERE THE ERROR HAPPENS
     console.log(items);
}, [state]);

当 state 改变时,添加对使用回调的依赖以重新计算

这是一个带有一些注释的完全重写

        import React, {useCallback, useEffect, useState} from "react";

/** Option One if the function does not requires variables from the component 
 * itself you can put it outside like in "api" folder */
const fetchPeople = async () => {
    //items ... sdk call
    
    // if items are already calculated and they are not async
    return items.map((item)=>({
        key: item.id,
        name: item.name,
        lastName: item.lastname
    }))

    // else 
    // return (await Promise.all(items)).map((item)=>({
    //     key: item.id,
    //     name: item.name,
    //     lastName: item.lastname
    // }))
}

export const ListComponent = (props: ListComponentProps) => {

    const [items, setItems] = useState<ITableEntry[]>([])

    // Option Two: use callback this function is "saved" inside a variable with a memoization based on the 
    // elements inside the array at the end
    // const fetchPeople = useCallback(async () => {
    //     ...
    // }, [])


    useEffect(() => {

        // option three you can also leave it there so it can be used in other part of the application 
        // const fetchPeople = async () => {
        //     ...
        // }

        // if you like async await toy can run this
        (async () => {
            setItems(await fetchPeople())
        })()

        /** if this is not modifiable you don't need to put it there 
         * and this function will run after the component is "mount" 
         * in my case fetch people will not change and that is why you should use useCallback
        */
    }, [fetchPeople]);

    const onColumnClick = useCallback((ev: React.MouseEvent<HTMLElement>, column: IColumn): void => {
        console.log(items);
    }, [items]);

    const columns = [
        {
            key: 'column1',
            name: 'First Name',
            fieldName: 'name',
            minWidth: 210,
            maxWidth: 350,
            isRowHeader: true,
            isResizable: true,
            isSorted: true,
            isSortedDescending: false,
            sortAscendingAriaLabel: 'Sorted A to Z',
            sortDescendingAriaLabel: 'Sorted Z to A',
            onColumnClick: onColumnClick,
            data: 'string',
            isPadded: true,
        },
        {
            key: 'column2',
            name: 'Last Name',
            fieldName: 'lastname',
            minWidth: 210,
            maxWidth: 350,
            isRowHeader: true,
            isResizable: true,
            isSorted: true,
            isSortedDescending: false,
            sortAscendingAriaLabel: 'Sorted A to Z',
            sortDescendingAriaLabel: 'Sorted Z to A',
            onColumnClick: onColumnClick,
            data: 'string',
            isPadded: true,
        },
    ]

    return (
        <>
            <DetailsList
                items={items}
                columns={columns}
            />
        </>
    );
});

保持变量尽可能简单,除非需要一些奇怪的东西,否则只需将“数据”保存在 State

这是一个真正使这项工作有效的修复程序!

所以我实际上找到了与我的问题类似的帖子(尽管我之前已经搜索了很长时间):

反应 - function 不打印当前状态

但是,必须将解决方案修改为此以反映列中的更改。 该解决方案还总是在更改项目时刷新列(请参阅useEffects,我在其中设置列),因此正在更新列。

export const ListComponent = (props: ListComponentProps) => {

    
    const [state, setState] = React.useState({
        items: [] as IDocument[],
        columns: [] as IColumn[],
      });

    const fetchPeople = React.useCallback(async () => {
        const entry: ITableEntry[] = [];

        //items ... sdk call

        for await (const item of items) {
            entry.push({
                key: item.id,
                name: item.name,
                lastName: item.lastname
            });
        }
  
        setState((state) => ({ ...state, items: elementsList }));
      }, []);

    useEffect(() => {
        setState((state) => ({ ...state, columns: columns }));
      }, [state.items]);

      
    useEffect(() => {
        fetchPeople();
    }, []);

    const _onColumnClick = React.useCallback((ev: React.MouseEvent<HTMLElement>, column: IColumn): void => {
        
        const columns = state.columns;
        const items = state.items;
        console.log(items);
    }, [state.items, state.columns]);


    const columns: IColumn[] = [
        {
            key: 'column1',
            name: 'First Name',
            fieldName: 'name',
            minWidth: 210,
            maxWidth: 350,
            isRowHeader: true,
            isResizable: true,
            isSorted: true,
            isSortedDescending: false,
            sortAscendingAriaLabel: 'Sorted A to Z',
            sortDescendingAriaLabel: 'Sorted Z to A',
            onColumnClick: _onColumnClick,
            data: 'string',
            isPadded: true,
        },
        {
            key: 'column2',
            name: 'Last Name',
            fieldName: 'lastname',
            minWidth: 210,
            maxWidth: 350,
            isRowHeader: true,
            isResizable: true,
            isSorted: true,
            isSortedDescending: false,
            sortAscendingAriaLabel: 'Sorted A to Z',
            sortDescendingAriaLabel: 'Sorted Z to A',
            onColumnClick: _onColumnClick,
            data: 'string',
            isPadded: true,
        },
    ];

    return (
        <>
            <DetailsList
                items={state.items}
                columns={state.columns}
            />
        </>
    );
});

暂无
暂无

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

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