Reducer接收新顺序数组Payload后状态未更新问题求助
Hey there, let's figure out why your state isn't updating even though the payload looks correct when using React Beautiful DnD!
Common Causes & Fixes
1. You're Using Indexes as Keys for Draggable Items
React relies on stable, unique keys to correctly diff and re-render components. If you're using array indexes as keys for your todo items, rearranging the array will change the keys, which can break React's re-rendering logic and make it seem like the state didn't update.
Bad Practice:
{todos.map((todo, index) => ( <Draggable key={index} draggableId={index.toString()}> {/* Todo item content */} </Draggable> ))}
Fix:
Use a unique, stable ID from your todo objects instead:
{todos.map(todo => ( <Draggable key={todo.id} draggableId={todo.id.toString()}> {/* Todo item content */} </Draggable> ))}
2. Verify Your Reducer is Returning a New State Reference
Even though your code looks correct, double-check that the new todos array has a different reference than the old one. Add logs to your reducer to confirm:
case "endDrag": console.log("Old todos reference:", state.todos); console.log("New payload reference:", action.payload); console.log("References are different?", state.todos !== action.payload); return { ...state, todos: action.payload };
If the log says References are different? false, your rearrange function isn't creating a new array. Double-check the function:
const rearrange = (array, source, destination) => { // Create a shallow copy of the original array const newArray = [...array]; const movedItem = newArray.splice(source, 1)[0]; newArray.splice(destination, 0, movedItem); return newArray; };
This should always return a new array reference—if it's not, make sure you're not passing a mutated array into the function elsewhere.
3. Your Component Might Be Blocking Re-Renders with React.memo
If you wrapped your todo list component with React.memo, ensure the comparison function isn't incorrectly skipping re-renders. For example:
// Bad: This will skip re-renders even if todos array content changes (but reference is same) const TodoList = React.memo(({ todos }) => { /* ... */ }, (prev, next) => prev.todos === next.todos );
Either remove the custom comparison function (let React do shallow comparison automatically) or update it to check array content if needed.
4. Ensure You're Using the Latest todos State in onDragEnd
If your onDragEnd handler is using a stale version of the todos state (from a closure), you might be dispatching an update based on old data. Make sure you're pulling the latest state directly from your context when handling the drag end:
const { todos, dispatch } = useContext(TodoContext); const onDragEnd = (result) => { if (!result.destination) return; // Use the latest todos state here, not a stale value const newArray = rearrange(todos, result.source.index, result.destination.index); dispatch({ type: "endDrag", payload: newArray }); };
内容的提问来源于stack exchange,提问作者Mattias Bregnballe

