首页 > 解决方案 > React Context - 未正确设置状态或从状态读取

问题描述

我正在使用 react-beautiful-dnd(https://github.com/atlassian/react-beautiful-dnd)库在列之间移动项目。将项目拖动到不同的列后,我正在尝试更新列的状态。在 endDrag 函数中,我在设置状态之前注销了列变量,此时它是正确的。所以要么我没有正确设置状态,要么我没有从列组件中正确读取状态。我在 OrderColumn 组件中的控制台日志正在输出列的旧状态,所以这似乎告诉我我没有正确设置状态。

这是我设置初始状态的上下文文件:

    import React, { createContext, useState } from 'react';

export const ScheduleContext = createContext();

export const ScheduleProvider = (props) => {
    const orderData = [
        ['First', 'First Order'],
        ['Second', 'Second Order'],
        ['Third', 'Third Order'],
        ['Fourth', 'Fourth Order'],
        ['Fifth', 'Fifth Order']
    ];

    const tempOrders = orderData.map(function(val, index) {
        return {
            id: index,
            title: val[0] + ' Order',
            desc: 'This order is for ' + val[1] + '.'
        };
    });

    const orderIDs = tempOrders.map(function(val) {
        return val.id;
    });

    const columnCount = 5;
    const cols = [];
    for (let i = 0; i < columnCount; i++) {
        cols.push({
            title: 'Line ' + i,
            columnID: 'column-' + i,
            orderIDs: i === 0 ? orderIDs : []
        });
    }

    // eslint-disable-next-line no-unused-vars
    const [orders, setOrders] = useState(tempOrders);
    // eslint-disable-next-line no-unused-vars
    const [columns, setColumns] = useState(cols);

    const contextValue = { orders, setOrders, columns, setColumns };

    return (
        <ScheduleContext.Provider value={contextValue}>
            {props.children}
        </ScheduleContext.Provider>
    );
};

接下来,这是我的 SchedulePage,它是 App.js 下的顶级组件。

import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import { DragDropContext } from 'react-beautiful-dnd';
import OrderColumn from '../ordercolumn/OrderColumn';
import { ScheduleContext } from '../../schedule-context';

const Schedule = () => {
    const { columns, setColumns } = useContext(ScheduleContext);
    const onDragEnd = (result) => {
        const { destination, source } = result;

        if (!destination) {
            return;
        }

        if (
            destination.droppableId === source.droppableId &&
            destination.index === source.index
        ) {
            return;
        }

        const column = columns.find(
            (col) => col.columnID === source.droppableId
        );

        const orderIDs = Array.from(column.orderIDs);
        orderIDs.splice(source.index, 1);

        const newColumn = {
            ...column,
            orderIDs: orderIDs
        };

        const ind = columns.indexOf(column);
        columns.splice(ind, 1, newColumn);
        console.log(columns);

        setColumns(columns);
    };

    const columnsArray = Object.values(columns);
    return (
        <DragDropContext onDragEnd={onDragEnd}>
            <div className={'full-width'}>
                <h1 className={'text-center'}>Schedule</h1>
                <div className={'lines row no-gutters'}>
                    {columnsArray.map(function(val, index) {
                        return (
                            <OrderColumn
                                key={index}
                                title={val.title}
                                columnId={val.columnID}
                                orderIDs={val.orderIDs}
                            />
                        );
                    })}
                </div>
            </div>
        </DragDropContext>
    );
};

Schedule.propTypes = {
    orders: PropTypes.array
};

export default Schedule;

最后是 SchedulePage 下的 OrderColumn 页面:

import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import { Droppable } from 'react-beautiful-dnd';
import styled from 'styled-components';
import Order from '../order/Order';
import { Scrollbars } from 'react-custom-scrollbars';
import { ScheduleContext } from '../../schedule-context';
import '../../App.css';

const MyOrder = styled.div``;
const OrderColumn = (props) => {
    const colId = props.columnId;
    const orders = useContext(ScheduleContext).orders;
    const orderIDs = useContext(ScheduleContext).columns.find(
        (col) => col.columnID === colId
    ).orderIDs;
    console.log('orderIDs: ');
    console.log(orderIDs);
    return (
        <Droppable droppableId={colId}>
            {(provided) => {
                console.log('orderIDs: ');
                console.log(orderIDs);
                return (
                    <MyOrder
                        className={'col order-column'}
                        ref={provided.innerRef}
                        {...provided.droppableProps}
                    >
                        <Scrollbars
                            // This will activate auto hide
                            autoHide
                            // Hide delay in ms
                            autoHideTimeout={1000}
                            // Duration for hide animation in ms.
                            autoHideDuration={200}
                        >
                            <h3 className={'text-center title'}>{props.title}</h3>
                            <div className={'orders'}>
                                {orderIDs &&
                                    orderIDs.map((orderID, index) => {
                                        const order = orders.find(
                                            (o) => o.id === orderID
                                        );
                                        return (
                                            <Order
                                                key={orderID}
                                                order={order}
                                                index={index}
                                            />
                                        );
                                    })}
                            </div>
                        </Scrollbars>
                        {provided.placeholder}
                    </MyOrder>
                );
            }}
        </Droppable>
    );
};

OrderColumn.propTypes = {
    orders: PropTypes.array,
    title: PropTypes.string.isRequired,
    columnId: PropTypes.string.isRequired
};

export default OrderColumn;

想出了如何让我的整个应用程序在线测试/查看是否对任何人有帮助:https ://codesandbox.io/s/smoosh-shape-g8zsp

标签: reactjsstate

解决方案


据我了解,您更改列,然后将其保存到状态。你的问题是你只是在改变你的状态:

columns.splice(ind, 1, newColumn); // <-- mutation of columns array
console.log(columns);

setColumns(columns); // <-- noop as react compares references of the arrays

为防止出现此问题,您应该先创建一个副本,然后修改并保存它。然后 React 将接受更改:

const adjustedColumns = columns.slice(); // or Array.from(columns)
adjustedColumns.splice(ind, 1, newColumn);
setColumns(adjustedColumns);

推荐阅读