Reputation: 1392
I am using the react-beautiful-dnd(https://github.com/atlassian/react-beautiful-dnd) library for moving items around between columns. I am trying to update my columns in state after I drag an item to a different column. In the endDrag function I am logging out the columns variable right before setting state and it is correct at that point. So either I'm not setting state correctly, or I am not reading from state correctly in the column component. My console log in the OrderColumn component is outputting the old state of columns, so that seems to tell me I'm not setting the state correctly.
Here is my context file where I set the initial state:
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>
);
};
Next, this is my SchedulePage which is the top component under 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;
Finally, the OrderColumn page which is under the SchedulePage:
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;
Figured out how to get my whole app online for testing/viewing if that helps anyone: https://codesandbox.io/s/smoosh-shape-g8zsp
Upvotes: 0
Views: 299
Reputation: 1968
As far as I understood, you change your columns and then save it to the state. Your problem is that you are just mutating your state:
columns.splice(ind, 1, newColumn); // <-- mutation of columns array
console.log(columns);
setColumns(columns); // <-- noop as react compares references of the arrays
To prevent this issue, you should just create a copy first and then modify and save it. React then will pick up the change:
const adjustedColumns = columns.slice(); // or Array.from(columns)
adjustedColumns.splice(ind, 1, newColumn);
setColumns(adjustedColumns);
Upvotes: 1