1

I am using recharts, and if the data variable is outside of a class, it works fine. What happens is it loads, animates the graph, and shows the "dot" coordinates. However, if the data variable is inside the class, it does not animate, and does not update the "dot" coordinates css.

Notice the commented out data in the render method, if I uncomment that, and comment out the top data variable, it doesn't work, but this current setup works just fine. Any ideas for a fix? I eventually want to load this.props.data instead of data once this is fixed.

const data = [{ name: '07/14/2017', mood: 6 }, { name: '07/15/2018', mood: 7 }];

class LinearGraph extends Component {

  constructor(props) {
    super(props);

  }

  render() {
    //const data = [{ name: '07/14/2017', mood: 6 }, { name: '07/15/2018', mood: 7 }];

    return (
      <ResponsiveContainer width="100%" height="80%">
        <LineChart
          data={data}
          margin={{ top: 5, right: 50, left: 0, bottom: 5 }}
        >
          <XAxis dataKey="name" />
          <YAxis />
          <CartesianGrid
            strokeDasharray="3 3"
            horizontal={false}
          />
          <Tooltip />
          <Legend />
          <Line
            type="monotone"
            dataKey="mood"
            stroke="rgba(43, 191, 217, 0.9)"
            dot={{ stroke: '#ff0000', strokeWidth: 12 }} // this isn't working at all
            activeDot={{ r: 1 }}
            strokeWidth={5}
          />
          <ReferenceLine y={7} label="Max" stroke="green" strokeDasharray="3 3" />
        </LineChart>
      </ResponsiveContainer>
    );
  }

}

also for a more visual understanding, in this photo it's when it works (can't show the animation, but you can see the "dot" coordinates are working):

enter image description here

And here it's not working: enter image description here

EDIT: I've also tried setting the state in componentWillMount (and componentDidMount but this latter gave a warning):

class LinearGraph extends Component {
  constructor(props) {
    super(props);
    this.state = {
      data: [],
    };
  }

  componentWillMount() {
    this.setState({ data: this.props.data });
  }

  render() {
    return (
      <ResponsiveContainer width="100%" height="80%">
        <LineChart data={this.state.data} />
      </ResponsiveContainer>
    );
  }

}
9
  • 1
    You need to follow the React lifecycle, which will rerender on prop and state changes. Can you define data as a property of your component's state? Commented Jul 19, 2017 at 21:06
  • If in the constructor I set this.state = { data: this.props.data}, it's undefined. now if I set it to be an empty array, I can't do this.setState({data: this.props.data}) in the render method, it throws tons of errors, and not sure where else I can set it due to the asynchronous loading. I can't make it a function in the class, and then call it in render for the same reason. @JordanBonitatis Commented Jul 19, 2017 at 21:10
  • I've also tried adding this.setState to componentWillMount and componentDidMount. It's bizarre because this.state.data is showing an empty array, and this.props.data is working just fine in the console logs within the methods, as it asynchronously updates. Commented Jul 19, 2017 at 21:18
  • You seem to be on the right track setting initialState in your constructor. If the library doesn't like an empty array, can you initiate it as an array w/ a single empty object, like data: [{}]? (Admittedly, w/o knowing the inner-workings of the library, that's a bit of a shot in the dark) Commented Jul 19, 2017 at 21:23
  • I completely agree with Jordan about React lifecycle. For the async data you need to use promise as shown by Kyle in his answer. When the data is fetched then the state will be set. Commented Jul 19, 2017 at 21:28

2 Answers 2

2

render is called on state changes. You need to brush up on the react component's lifecycle. Best practice is to declare your state then load it in a lifecycle componentDidMount function.

class LinearGraph extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            data = []
        };
    }

    componentDidMount() {
        // Get your data here, from a const or an API
        fetch('api/get_data')
            .then(res => res.json()) // Get the json
            .then(res => this.setState({data: res.data})) // Consume the json
            .catch(err => alert('foo'));
    }

    render() {
        return (
            <div>
                <LineChart data={this.state.data} />
            </div>
            );
    }
}
Sign up to request clarification or add additional context in comments.

7 Comments

Cool, I was actually playing around with this before you posted. Would it be possible to have componentDidMount simply have this.setState({data: this.props.data}), as the fetch is occurring in the parent component and is passing data to other children as well? Because I tried and it didn't work
actually it's giving me a warning and saying "do not set state in component did mount"
This makes a lot more sense than my commented approach; I was assuming the data was static/hardcoded
Try componentWillMount
I tried both componentWillMount and componentDidMount. I just updated my original post in the bottom with an EDIT of the update. Still not working :(
|
0

Woo, so the solution was to call componentWillReceiveProps, so I just added:

componentWillReceiveProps() { this.setState({ data: this.props.data }); }

and it's now updating nicely, without the need for callbacks or promises

1 Comment

Welp, nevermind. this works for the initial load, but if I change pages on my site and come back, same problem. I see the graph line, but the dots do not update to appropriate css color.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.