I have 3 components.
In ListCard.js, I map cards array and based on the card the user click on, I call handleChangeCardData to update the modal's text.
My question is: How do I update/change the modal's text when my handleChangeCardData function is inside ListCard.js and my modal is on the same level. (Both are in Board.js)
Board.js
const [cardTitle, setCardTitle] = useState("");
return (
{columns.map((column, index) => (
<div className="column__container" key={index}>
<div className="column__header">
<div className="columnHeader__name">
<p>{column.name ? column.name : "..."}</p>
</div>
<div className="columnHeader__button">
<button
className="btn btn-sm --create-card-btn"
data-bs-toggle="modal"
data-bs-target="#modal-card"
onClick={() => setColumnId(column.id)}
>
New item
</button>
</div>
</div>
<Droppable droppableId={column.id}>
{(provided, snapshot) => (
<div
className="column"
ref={provided.innerRef}
{...provided.droppableProps}
>
<ListCard columnId={column.id} />
{provided.placeholder}
</div>
)}
</Droppable>
</div>
))}
<ViewCardModal cardTitle={cardTitle} />
)
LisCard.js
const handleChangeCardData = (cardTitle) => {
setCardTitle(cardTitle);
}
return (
{cards.map((card, index) => (
<>
<div key={index}>
<Draggable draggableId={card.id} index={index}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<div
className="card --listcard-card"
onClick={() => handleChangeCardData(card.title)}
data-bs-toggle="modal"
data-bs-target="#modal-card-details"
style={{ border: `2px solid ${card.color}` }}
>
<div className="card-body">
<p>{card.title}</p>
</div>
</div>
</div>
)}
</Draggable>
</div>
</>
))}
)
ViewCardModal.js
function ViewCardModal(props) {
return (
<div>{props.cardTitle}</div>
)
}
In general, lift state up. In this case, it sounds like that means moving the state into Board
and then passing that state to whatever child components need it (as a prop), and the state setter to whatever (other) child components need it.
Here's a minimal example of lifting state up. I haven't tried to recreate the full complexity of your example, just to provide an example of Parent
having state that ChildA
uses and ChildB
sets:
const {useState} = React;
const ChildA = React.memo(({counter}) => {
console.log("ChildA rendered");
return <div>Counter = {counter}</div>;
});
const ChildB = React.memo(({setCounter}) => {
console.log("ChildB rendered");
return <input
type="button"
value="Increment Counter"
onClick={() => setCounter(c => c + 1)}
/>;
});
const Parent = () => {
const [counter, setCounter] = useState(0);
return (
<div>
<ChildA counter={counter} />
<ChildB setCounter={setCounter} />
</div>
);
};
ReactDOM.render(<Parent />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.development.js"></script>
If there are several levels of hierarchy between where the state is being held and a descendant component that needs it, you might use context instead of props (although you might also look at component composition instead). See those links for details.