rednate95
rednate95

Reputation: 53

Using redux state in local state

I'm trying figure out why I can't define my redux state into my local state. Basically I'm try to give displayed the value of this.props.tournaments so I can manipulate the state. But every time I define it and console.log(this.state.displayed) as shown in the renderList function, it spits out an empty array however if I console.log(this.props.tournaments) I get the data I need from my api, any ideas?

Here is what I'm working with:

the main component

import { connect } from 'react-redux';
import axios from 'axios';

import { fetchTournaments } from '../actions/tournaments';
import Item from './Item';
import EditTournament from './EditTournament';

import '../styles/Item.css';

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

    this.state = {
      searchTerm: '',
      displayed: this.props.tournaments
    };
  }

  componentDidMount() {
    console.log(this.state.displayed, 'here');
    this.props.fetchTournaments();
  }

  async handleDelete(id) {
    const url = `http://localhost:4000/tournaments/`;

    await axios
      .delete(url + id)
      .then(res => {
        console.log(res.data);
      })
      .catch(err => {
        console.log(err);
      });
  }

  renderList() {
    let array = this.props.tournaments;

     console.log(this.state.displayed);
     console.log(this.props.tournament);

    return array.map(tournament => {
      return tournament.map(item => {
        if (!this.props.tournaments) {
          return <div className="button">Loading....</div>;
        } else {
          return (
            <Item
              key={item.id}
              name={item.name}
              organizer={item.organizer}
              participants={Object.values(item.participants)}
              game={item.game}
              start={item.startDate}
            >
              <div className="buttonBar">
                <EditTournament id={item.id} name={item.name} />
                <button
                  className="button"
                  onClick={() => {
                    if (
                      window.confirm(
                        'Are you sure you want to delete this item?'
                      )
                    ) {
                      this.handleDelete(item.id);
                    }
                  }}
                >
                  Delete
                </button>
              </div>
            </Item>
          );
        }
      });
    });
  }

  render() {
    return (
      <React.Fragment>
        <div className="row">{this.renderList()}</div>
      </React.Fragment>
    );
  }
}

function mapStateToProps({ tournaments }) {
  return {
    tournaments: Object.keys(tournaments).map(key => tournaments[key])
  };
}

export default connect(mapStateToProps, {
  fetchTournaments
})(List);

what it looks like after I console.log(this.props.tournaments):

console result

what it looks like after I console.log(this.state.displayed)

console result

Data from API:

[
  {
    "id": "9c430783-dc3a-4114-954a-c041e9350c81",
    "name": "Fuga Repellendus Quibusdam Neque",
    "organizer": "Perferendis Omnis",
    "game": "Counter-Strike: Global Offensive",
    "participants": {
      "current": 190,
      "max": 256
    },
    "startDate": "2020-06-12T18:58:14.837Z"
  },
  {
    "id": "1919af3f-cde0-4ca4-83fa-e0ae1cfa9a9c",
    "name": "Qui Voluptatem Impedit",
    "organizer": "Dolore Quae",
    "game": "League of Legends",
    "participants": {
      "current": 182,
      "max": 256
    },
    "startDate": "2020-06-12T18:58:14.837Z"
  },
  {
    "id": "4b806324-bc63-4fb0-a30e-c3a42099c510",
    "name": "Earum Ut Omnis",
    "organizer": "Officia Cupiditate",
    "game": "Rocket League",
    "participants": {
      "current": 56,
      "max": 256
    },
    "startDate": "2020-06-12T18:58:14.837Z"
  },
]

my redux actions

import {
  FETCH_TOURNAMENTS,

} from './types';

import { API_TOURNAMENTS_URL } from '../constants/api';
import axios from 'axios';

export const fetchTournaments = () => async dispatch => {
  const response = await axios.get(API_TOURNAMENTS_URL);

  dispatch({ type: FETCH_TOURNAMENTS, payload: response.data });
};

my reducers

import {
  FETCH_TOURNAMENTS,
} from '../actions/types';

export default (state = {}, action, value: '') => {
  switch (action.type) {
 
    case FETCH_TOURNAMENTS:
      return { ...state, [action.payload.id]: action.payload };
 
    default:
      return state;
  }
};

Upvotes: 0

Views: 896

Answers (4)

Vivek Doshi
Vivek Doshi

Reputation: 58553

First, data won't be available when your constructor gets called,

And you are initializing the state inside the state, so if you want to update state also whenever there is a change in props, You can use :

componentDidUpdate(nextProps) {
  // set your state here
}

But, I don't suggest this

As you are already mapping stateToProps, that will cause the re-render as there are any changes in your tournaments, so you can use that props only for displaying purpose and if you want to make any change you should dispatch event, this is how you should do in react-redux architecture.

And your console.log showing the correct result, this is how it works

Upvotes: 1

Mrinmay
Mrinmay

Reputation: 153

This is because of two reasons :

  1. Since value of tournaments is set asynchronously in redux upon calling the function fetchTournaments, the initial value of displayed in local state will be the initial value of tournaments in redux i.e. empty array.
  2. Constructor is run only once, so even after data is fetched and in redux you have new value for tournaments. Your state value remains unaffected. Hence the undefined in console.log

In order to remedy this,

  1. You should set the state of displayed only when there is a change in the value of tournaments prop. Like so -
componentWillReceiveProps(nextProps) {
 // compare this.props and nextProps and set state here
}

Additionally, as a general practise, I would recommend not setting the value of state to a prop in constructor, instead use initial state. i.e.

this.state = {
  searchTerm: '',
  displayed: []
}

Upvotes: 0

Oro
Oro

Reputation: 2586

It is about life cycle of the React component. Your constructor method called just once when the component was created and won't update after updating your props.

componenedDidMount method updates your redux store but doesn't touch your local state. You can sync your redux state and local state in componentDidUpdate method of your component. Don't use componentWillReceiveProps It's deprecated

Upvotes: 0

Red Baron
Red Baron

Reputation: 7652

I wouldn't do this:

this.state = {
  searchTerm: '',
  displayed: this.props.tournaments
};

instead use getDerivedStateFromProps function or setState inside componentDidMount

the reason it isn't showing is because setState is async

setState takes a second argument of a callback that you could use to be sure that state has been set and then call a function in the callback

Upvotes: 0

Related Questions