I'm having a very weird issue where just one component on a page is not being refreshed, and I just can't figure out why.
Here's a short video of the problem:
https://i.gyazo.com/45e229b0867c37e48a18da7a55afb522.mp4
Notice how the question string changes when I click confirm (as it should), but the cards of the drag and drop window stay the same. It keeps displaying the question name "yooo" and the answers "abc, def", while that's only valid for the first question.
I'm still relatively new to ReactJS, so there may be some functionality here that I'm not familiar with? As far as I know DragAndDrop should be re-rendered entirely with the next question. Currently the constructor is not being called again and it saves the data of the last question somehow.
Render of this page. DragAndDrop is being called here. In confirm(), currentQuestion is being set to the next question.
return (
<div>
<h3>{currentQuestion.question}</h3>
<DragAndDrop
answers={currentQuestion.answers}
/>
<Button onClick={() => confirm()}>Confirm</Button>
</div>
);
Entire DragAndDrop.js Sorry about the wall of code, it's almost the same as the example code from Beautiful-DND https://codesandbox.io/s/k260nyxq9v
/* eslint-disable no-console */
/* eslint-disable react/prop-types */
import React, { Component } from "react";
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
// STYLING
const grid = 8;
const getItemStyle = (isDragging, draggableStyle) => ({
// some basic styles to make the items look a bit nicer
userSelect: "none",
padding: grid * 2,
margin: `0 0 ${grid}px 0`,
// change background colour if dragging
background: isDragging ? "cyan" : "white",
// styles we need to apply on draggables
...draggableStyle,
});
const getListStyle = (isDraggingOver) => ({
background: isDraggingOver ? "lightblue" : "lightgrey",
padding: grid,
width: "100%",
});
// a little function to help us with reordering the result
const reorder = (list, startIndex, endIndex) => {
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
};
export default class DragAndDrop extends Component {
constructor(props) {
super(props);
this.state = {
items: props.answers,
};
this.onDragEnd = this.onDragEnd.bind(this);
console.log("Answers & items");
console.log(this.props.answers);
console.log(this.state.items);
}
onDragEnd(result) {
// dropped outside list
if (!result.destination) {
return;
}
const items = reorder(
this.state.items,
result.source.index,
result.destination.index
);
this.setState({
items,
});
}
render() {
return (
<DragDropContext onDragEnd={this.onDragEnd}>
<Droppable droppableId="droppable">
{(provided, snapshot) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
style={getListStyle(snapshot.isDraggingOver)}
>
{this.state.items.map((item, index) => (
<Draggable
key={item.id}
draggableId={item.id.toString()}
index={index}
>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={getItemStyle(
snapshot.isDragging,
provided.draggableProps.style
)}
>
{
item.answer +
" index: " +
index +
" ordering:" +
item.ordering /*CONTENT OF CARD*/
}
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
);
}
}
I think the problem lies in this line in the
constructor
:Setting
items
like this in the constructor means you are ignoring any following updates toprops
from the parent component! If you check the official documentation, they warn against this.If you want to depend on the values of the
props
and change the state accordingly, you can usestatic getDerivedStateFromProps()
Here is a working example using a class-based component which is just a proof-of-concept using your component with
static getDerivedStateFromProps()
(which is not deprecated!). I added some dummy data that uses the same structure you provided in the parent component which changes when you click "Confirm". Also, here is a working example using hooks doing the same thing which usesuseState
anduseEffect
hooks.