1

I have a WebSocket that's feeding data to the client and, i'm displaying it (data) in a chart using D3 in react, and so i want my chart to change according to the data, but to change the state of the chart component, i have to access it from the parent component where the onMessage event is defined, my problem is that the chart doesn't update :

Parent Component :

class App extends Component {
constructor(props) {
    super(props);
    this.state = {
      data: [],
    };
  }
  handleData(data) {
      let result = JSON.parse(data);
      this.setState((state) => {
        state.data.push(result.c.gas);
      });
  }

  render() {
    return (
      <div>
        <Websocket
          url={`ws://${window.location.host}/ws/mqtt_app/1/`}
          onMessage={this.handleData.bind(this)}
        />
        <Chart data={this.state.data} />
      </div>
    );
  }
}

Child component :

const Chart = (props) => {
  const [data, setData] = useState(props.data);
  const svgRef = useRef();

  useEffect(() => {
    // chart definition
  }, [data]);

  return (
    <React.Fragment>
      <svg ref={svgRef}>
        <g className="x-axis"></g>
        <g className="y-axis"></g>
      </svg>
    </React.Fragment>
  );
};

export default Chart;

Thank you

3 Answers3

0

The problem is here

const [data, setData] = useState(props.data);
const svgRef = useRef();

useEffect(() => {
  // chart definition
}, [data]);

On first render of your Chart component you create local data with the data from parent component. With useEffect you're watching that local data (not data coming from parent), but it never changes because you never invoke setData inside of your Chart component. If it did work, local state wouldn't be possible.

Solution is to use directly the data from parent and put your // chart definition directly in the Chart, not in useEffect

const Chart = (props) => {
  const svgRef = useRef();
  // chart definition

  return (
    <React.Fragment>
      <svg ref={svgRef}>
        <g className="x-axis"></g>
        <g className="y-axis"></g>
      </svg>
    </React.Fragment>
  );
};

export default Chart;
fila90
  • 1,439
  • 11
  • 11
0

thanks to @Mario in the comments i've found the problem, first I removed the state from the child component, and used the props in useEffect, and after that as @Mario said i changed

     this.setState((state) => {
        state.gas.push(result.c.gas);
      });

with

this.setState((state) => ({ gas: [...state.gas, result.c.gas] }));

because, the state wasn't even changing, i only added an element to the gas array that i already had, but for the state to change a direct property should be resigned.

0

You need to assign a new value to the state variable. Please try this

this.setState(state => ({ data: [...state.data, result.c.gas] }))

In your case you were mutating its value

Mario
  • 4,784
  • 3
  • 34
  • 50