Reputation: 53
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):
what it looks like after I console.log(this.state.displayed)
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
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
Reputation: 153
This is because of two reasons :
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.tournaments
. Your state value remains unaffected. Hence the undefined
in console.log
In order to remedy this,
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
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
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