0
votes

I have a component in which I fetch data based on an item ID that was clicked earlier. The fetch is successful and console.log shows the correct data, but the data gets lost with this.setState. I have componentDidUpdate and componentDidMount in the same component, not sure if this is okay or maybe these two are messing eachother up?

Here is the code:

const teamAPI = 'http://localhost:8080/api/teams/'
const playerAPI = 'http://localhost:8080/api/playersByTeam/'
const matchAPI = 'http://localhost:8080/api/match/'

class View extends Component {

  constructor() {
    super();
    this.state = {
      data: [],
      playersData: [],
      update: [],
      team1: [],
      team2: [],
      matchData: [],
      testTeam: [],
    };
  }

  componentDidUpdate(prevProps) {
    if (prevProps.matchId !== this.props.matchId) {
      fetch(matchAPI + this.props.matchId)
      .then((matchResponse) => matchResponse.json())
      .then((matchfindresponse) => {
        console.log(matchfindresponse);
        this.setState({
          matchData:matchfindresponse,
          testTeam:matchfindresponse.team1.name,
        })
      })
    }
  }

  componentDidMount() {
    fetch(teamAPI)
    .then((Response) => Response.json())
    .then((findresponse) => {
      console.log(findresponse)
      this.setState({
        data:findresponse,
        team1:findresponse[0].name,
        team2:findresponse[1].name,
      })
    })

    fetch(playerAPI + 82)
    .then(playerResponse => playerResponse.json())
    .then(players => {
      console.log(players)
      this.setState({
        playersData:players
      })
    })
  }

The first render also gives this warning:

Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op.

Please check the code for the View component.

Everything from ComponentDidMount works fine in render but {this.state.matchData} and {this.state.testTeam} from componentDidUpdate are empty.

Could the problem be that ComponentDidMount re-renders the component which causes the data from ComponentDidUpdate to be lost and if so, how could I fix this?

Tried ComponentWillReceiveProps like this but still no luck

  componentWillReceiveProps(newProps) {
    if (newProps.matchId !== this.props.matchId) {
      fetch(matchAPI + newProps.matchId)
      .then((matchResponse) => matchResponse.json())
      .then((matchfindresponse) => {
        console.log(matchfindresponse.team1.name);
        console.log(this.props.matchId + ' ' + newProps.matchId);
        this.setState({
          matchData:matchfindresponse.team1.name,
        })
      })
    }
  }
2
:o Sorry, maybe I worked on a too old version of React... Last time I used it, i'm pretty sure I had to merge like that! Removing it, sorry again... I'm gonna take a deeper look! - sjahan
It looks like you should use componentWillReceiveProps and not componentDidUpdate - Milos Mosovsky

2 Answers

2
votes

On your componentDidMount you should be using Promise.all. This isn't really your problem, but it does make more sense.

componentDidMount() {
  const promises = [
    fetch(teamAPI).then(resp => resp.json()),
    fetch(playerAPI + 82).then(resp => resp.json())
  ];
  Promise.all(promises).then(([teamData, playerData]) => {
   // you can use this.setState once here
  });
}

Looks like your componentDidUpdate should be a getDerivedStateFromProps in combination with componentDidUpdate (this is new to react 16.3 so if you are using an older version use the depreciated componentWillReceiveProps). Please see https://github.com/reactjs/rfcs/issues/26. Notice too that now componentDidUpdate receives a third parameter from getDerivedStateFromProps. Please see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html for more details.

EDIT: Just to add more details. Your state object should just include other key like matchIdChanged. Then

// in your state in your constructor add matchId and matchIdChanged then
static getDerivedStateFromProps(nextProps, prevState) {
 if (nextProps.matchId !== prevState.matchId) {
   return { matchIdChanged: true, matchId: nextProps.matchId }
 }
 return null;
}

componentDidUpdate() {
  if (this.state.matchIdChanged) {
    fetch(matchAPI + this.props.matchId)
      .then((matchResponse) => matchResponse.json())
      .then((matchfindresponse) => {
        console.log(matchfindresponse);
        this.setState({
          matchData:matchfindresponse,
          testTeam:matchfindresponse.team1.name,
          matchIdChanged: false // add this
        })
      })
  }
}
0
votes

instead of using componentDidUpdate() lifecycle hook of react try using getDerivedStateFromProps() lifecycle function if you are using react 16.3, else try using componentWillReceiveProps() for below versions. In my opinion try to avoid the use of componentDidUpdate().

Plus error you are getting is because, setState() function is called, when your component somehow gets unmounted, there can be multiple reasons for this, most prominent being -

  1. check the render function of this component, are you sending null or something, based on certain condition?

  2. check the parent code of component, and see when is the component getting unmounted.

Or you can share these code, so that we might help you with this.

Plus try to debug using ComponentWillUnmount(), put console.log() in it and test it for more clarity.

Hope this helps, thanks