I have a child component, it looks through and creates Canvas elements in the DOM, then useEffect() draws things to these Canvases:
import { useEffect } from "react"; function Table(props) { console.log(">> In Table, props is ", props); useEffect(() => { console.log(">> in useEffect "); // prepare the data to render here and render to the multiple HTML Canvases in the DOM }, []); const options = [ { value: 0, label: "1" }, { value: 1, label: "2" } ]; const onChannelXChange = (option) => { console.log("1. send the change back to the parent"); let change = { type: "ChannelIndexChange", // TODO need to get the plot here plotIndex: 0, channel: "x", value: option.value, }; props.parentCallback(change); }; return ( <table className="workspace"> <tbody> <tr key={`tr-${fileIndex}`}> {props.workspaceState.plots.map((plot, plotIindex) => { return ( <td key={`td-${plotIindex}`}> <div> <canvas className="canvas" id={`canvas-${fileIndex}-${plotIindex}`} width="400" height="400" /> <Dropdown options={options} onChange={onChannelXChange} placeholder="Select an option" /> </div> </td> ); })} </tr> </tbody> </table> ); } export default Table;
And it’s parent component:
import Table from "./Table"; import React, { useState } from "react"; class Workspace extends React.Component { constructor(props) { super(); this.state = { workspaceState: {}, }; this.state.workspaceState = props.workspaceState; this.handleCallback = this.handleCallback.bind(this); } handleCallback = (option) => { this.props.workspaceState.value = option.value; // I expect this to re-render the Table Component with the updated props console.log("2. updating state"); this.setState({ workspaceState: this.props.workspaceState }); }; render() { return ( <Table enrichedEvents={this.props.enrichedEvents} workspaceState={this.props.workspaceState} className="workspace" parentCallback={this.handleCallback} ></Table> ); } } export default Workspace;
When the suer clicks on the Dropdown, I pass the value back up to the parent component (Workspace). This then updates the Workspace state, and I then expect the child coponent to be re-rendered – except it is not. When I look at the logs, I see:
Workspace.js:44 1. send the change back to the parent Workspace.js:44 2. updating parent state component Table.js:95 >> props is {workspaceState: {...}}
But I dont see:
>> in useEffect
I only see this log the first time the app runs. The Table component is indeed getting the new updated props, but it wont re-render with this new data. What am I doing wrong?
Advertisement
Answer
useEffect(() => {}, []) replace the componentDidMount in old react versions that means it execute only once after mounting the component in DOM. I am wondering if you really need a useEffect in your case , if it the case you need to use a useEffect without array of dependencies. LIke that:
import { useEffect } from "react"; function Table(props) { console.log(">> In Table, props is ", props); useEffect(() => { console.log(">> in useEffect "); // prepare the data to render here }); const options = [ { value: 0, label: "1" }, { value: 1, label: "2" } ]; const onChannelXChange = (option) => { console.log("1. send the change back to the parent"); props.parentCallback(option); }; return ( <Dropdown options={options} onChange={onChannelXChange} placeholder="Select an option" /> ); } export default Table;
Solution 2: As i said am wondering if you really need a useEffect you can directly do it like that
import { useEffect } from "react"; function Table(props) { console.log(">> In Table, props is ", props); // put you logic directly here // prepare the data to render here const options = [ { value: 0, label: "1" }, { value: 1, label: "2" } ]; const onChannelXChange = (option) => { console.log("1. send the change back to the parent"); props.parentCallback(option); }; return ( <Dropdown options={options} onChange={onChannelXChange} placeholder="Select an option" /> ); } export default Table;