Search code examples
reactjsreact-beautiful-dnd

How to Remap Array of Objects when State gets updated in React JS?


I am a newbie trying to mess around React, I cam across a problem on which I have been stuck for like 2 days is that I have an Array (state array to be precise) in which I add values by SetArray method, all works fine and nice, however I am also assigning that Array values to another array and mapping the second array. Now the problem is the first array gets updated everywhere the second in which I assign values from first one doesn't render immediately, it renders only when the whole section gets rendered.

Setting Values in Array 1 on Click (is has complicated OnClick function as well, haven't added here, this works fine btw)

const [globalFLZ, setGlobalFLZ] = useState([]);
const addGlobalFLZ = (obj) => {
    const idd = 1;
    const newGlobalFLZ = { idd, ...obj };
    setGlobalFLZ([...globalFLZ, newGlobalFLZ]);
}

Assigning values from Array 1 in Array 2

const weekColumns = {
    [uuid()]: {
        name: "All Items",
        items: globalFLZ              // assigning here
    },
    [uuid()]: {
        name: "Week 1",
        items: []
    },
    [uuid()]: {
        name: "Week 2",
        items: []
    },
    [uuid()]: {
        name: "Week 3",
        items: []
    },
    [uuid()]: {
        name: "Week 4",
        items: []
    },
    [uuid()]: {
        name: "Week 6",
            items: []
    }
};

Code to Map Array 2

As this is nested array, first columns get mapped then items inside it

{Object.entries(columns).map(([columnId, column], index) => {
    return (
        <Box
            boxShadow={1}
            key={columnId}
            className="diDragableColumn"
        >
                <Box className="diDragableColumnTitle" boxShadow={1}><h3>{column.name}</h3></Box>

                <Droppable droppableId={columnId} key={columnId} direction="vertical">
                    {(provided, snapshot) => {
                        return (
                            <div
                                {...provided.droppableProps}
                                ref={provided.innerRef}
                                style={{
                                    background: snapshot.isDraggingOver
                                        ? "lightgrey"
                                        : "",
                                    minHeight: '100%',
                                }}
                                className="diDropable"
                            >
                                {column.items.map((item, index) => {   // the problem is here
                                    return (
                                        <Draggable
                                            key={item.id}
                                            draggableId={`dragID`+item.id}
                                            index={index}
                                        >
                                            {(provided, snapshot) => {
                                                return (
                                                    <Tooltip 
                                                        title={item.answerAusformuliert}
                                                        arrow 
                                                        placement="top" 
                                                        className="di-tooltip"
                                                        classes={{
                                                            tooltip: classes.tooltip,
                                                            arrow: classes.arrow
                                                        }} 
                                                    >
                                                        <div
                                                            ref={provided.innerRef}
                                                            {...provided.draggableProps}
                                                            {...provided.dragHandleProps}
                                                            style={{
                                                                margin: "0 7px 7px 0",
                                                              ...provided.draggableProps.style
                                                            }}
                                                            className="diDraggableItem"
                                                        >

                                                            {item.answerAusformuliert}
                                                            {item.id}
                                                            
                                                            
                                                        </div>
                                                        </Tooltip>
                                                );
                                            }}
                                        </Draggable>
                                    );
                                })}
                                {provided.placeholder}
                            </div>
                        );
                    }}
                </Droppable>
            </Box>
    );
})}

Required: column.items.map should automatically re-map as Array 1 gets updated.

Sorry for the long and messy question but I am stuck bad any help would be greatly appreciated.

Thanks in advance.

fyi: I am using react-beautiful-dnd

EDIT Please check here: https://codesandbox.io/s/silent-haze-wvrv7


Solution

  • The problem is in your DragDrop component. Your columns state isn't updated when new sendGlobalFLZ is sent via props. This is because the useState only runs once for initializing the value and then only updates the value when set[StateVar] is used.

    You can fix this by implemented useEffect and providing sendGlobalFLZ as a dependency. This will cause it to re-run and update your columns everytime it receives a new memory address for sendGlobalFLZ.

    const [allItemsUUID] = useState(() => uuid());
    const [columns, setColumns] = useState(() => ({
      [allItemsUUID]: {
        name: "All Items",
        items: sendGlobalFLZ
        // items: staticData
      },
      [uuid()]: {
        name: "Week 1",
        items: []
      },
      [uuid()]: {
        name: "Week 2",
        items: []
      },
      [uuid()]: {
        name: "Week 3",
        items: []
      },
      [uuid()]: {
        name: "Week 4",
        items: []
      },
      [uuid()]: {
        name: "Week 5",
        items: []
      }
    }));
    
    useEffect(() => {
      const updatedColumns = {
        ...columns,
        [allItemsUUID]: {
          ...columns[allItemsUUID],
          items: sendGlobalFLZ
        }
      };
    
      setColumns(updatedColumns);
    }, [sendGlobalFLZ]);
    

    Full changes here