Skip to content
Advertisement

Child component not re-rendering with updated props

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?

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;
Advertisement