0

I have a modal displaying data that I'm receiving in props. When I open the modal, I should see select data displayed from my props. However, the modal is empty the first time I open it, and populates the second time.

If I go on to change the data in props, the modal stays the same on the first new click, and refreshes on the second new click.

I've tried forcing it with setTimeout, messing with combos of componentDidMount, componentDidUpdate, and other lifecycle methods, but nothing seems to work. I'm sure it has something to do with my using the prevData param in componentDidMount. But even thought react devtools shows this.state.pricesData updates, when I try rendering from state I get blanks every time. When I invoke a console log as a callback of setState, I get an empty array bracket in the console log (which I can expand to show all the correct array data, but I guess that's populated async after the log).

Here's the code:

import React, { Component } from "react";

import "../../App.css";

let explanations = [];

export default class ExplanationModal extends Component {
  constructor(props) {
    super(props);
    this.state = {
      pricesData: [],
    };
  }

  static getDerivedStateFromProps(nextProps, prevState) {
    if (nextProps.pricesData !== prevState.pricesData) {
      return { pricesData: nextProps.pricesData };
    } else {
      return null;
    }
  }

  // to allow for async rendering
  getSnapshotBeforeUpdate(prevProps) {
    if (prevProps.pricesData !== this.state.pricesData) {
      return this.state.pricesData;
    }
  }

  componentDidMount = () => {
    this.setState({ pricesData: this.props.pricesData }, () =>
      console.log(this.state.pricesData)
    );
  };

  componentDidUpdate = (prevData) => {
    this.renderExp(prevData.pricesData);
  };

  renderExp = (data) => {
    explanations = [];
    data.forEach((set) =>
      explanations.push({ title: set.titel, explanation: set.explenation })
    );
  };

  onClose = () => {
    this.props.hideModal();
  };

  render() {
    return (
      <div className="modal">
        <div>
          {explanations.map((item) => (
            <span>
              <h4>{item.title}</h4>
              <p>{item.explanation}</p>
            </span>
          ))}
        </div>
        <button onClick={this.onClose} className="update">
          Close
        </button>
      </div>
    );
  }
}
crevulus
  • 1,658
  • 12
  • 42

1 Answers1

2

you have to keep your explanation array in your state. then update the state when new data arrives. because react doesn't trigger a re render if you don't update the state .

your constructor should be

    super(props);
    this.state = {
      pricesData: [],
      explanations : []
    };
  }

and your renderExp function should be

renderExp = (data) => {
    explanations = [];
    data.forEach((set) =>
      explanations.push({ title: set.titel, explanation: set.explenation })
    );
    this.setState({ explanations })
  };

inside your render function

render() {
    return (
      <div className="modal">
        <div>
          {this.state.explanations.map((item) => (
            <span>
              <h4>{item.title}</h4>
              <p>{item.explanation}</p>
            </span>
          ))}
        </div>
        <button onClick={this.onClose} className="update">
          Close
        </button>
      </div>
    );
  }
}

This way you will get the updated data when it arrives.

Mohammad Faisal
  • 2,265
  • 1
  • 10
  • 16
  • 1
    Thanks for your feedback! The problem I think face is exceeding maximum update depth. Seeing as I'm calling `renderExp` in `componentDidMount`, and in `renderExp` I'm setting state, it gets stuck in an update/re-render loop. That's why I was saving explanations outside the class. Any ideas how to get around that? – crevulus Sep 22 '20 at 10:00
  • 1
    Follow up: called `renderExp` in `componentDidMount` using `this.state.pricesData` instead and worked a treat. Thank you! – crevulus Sep 22 '20 at 10:02