In my reducer, it returns an array of objects that i got from an api. I do a console.log on the list and I'm able to see the array, but when I get access to the reducer in my react class, it shows up as an empty array, why is that so? Inside the render() function in my react file, it does print for some odd reason, but I have a function where I'm trying to render seperate divs using that data from the reducer and the array shows up empty.
getList() {
let arr = [];
if(this.props.popular){
arr = this.props.popular.map(item => {
return (
<div key={item.id} className="movie">
<img
src={`${item.poster_path}`}
//onClick={() => this.displayModal(item)}
/>
</div>)
})
}
// console.log(arr)
// this.props.updateCurrentShowList(arr);
return arr;
}
I use this.props.popular from the mapstatetoprops function i have below.
import { FETCH_POPULAR, RESET_POPULAR } from "../Actions/types";
let initialList = [];
export default function(state = initialList, action){
switch(action.type){
case FETCH_POPULAR:
//return action.payload || false;
initialList = initialList.concat(...action.payload);
//console.log(initialList);
return initialList;
case RESET_POPULAR:
initialList = action.payload;
return initialList;
default:
return state;
}
}
Here the initialList is printed and works and i then return it.
This is my mapStateToProps function that i have in my other file where I want to get access to the array. I used binereducers in one of my reducers file.
function mapStateToProps(state) {
return {
popular: state.popular
};
}
Why does this.props.popular print correctly when i do it in render(), but whenever i use it anywhere else, it doesnt?
action function
export const fetchPopular = (searchTypeFormat, page) => async (dispatch) => {
let url = `/${searchTypeFormat}?api_key=${APIKEY}&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=${page}`;
//console.log(url);
const res = await axios.get(url);
//console.log(res.data.results)
dispatch({type: FETCH_POPULAR, payload: res.data.results});
};
my store creation
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import reducers from './Reducers/index';
import reduxThunk from 'redux-thunk';
const store = createStore(reducers, {}, applyMiddleware(reduxThunk));
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'));
I bined my reducers this way
import { bineReducers } from 'redux';
import authReducer from './authReducer';
import popularReducer from './popularReducer';
import genreListReducer from './genreListReducer';
import searchByGenreReducer from './searchByGenreReducer';
import { reducer as formReducer } from 'redux-form';
import modalReducer from './modalReducer';
import detailsReducer from './moreDetailReducer';
import userDisplayList from './userDisplayList';
export default bineReducers({
auth: authReducer,
form: formReducer,
popular: popularReducer,
genreList: genreListReducer,
searchByGenre: searchByGenreReducer,
modalData: modalReducer,
details: detailsReducer,
displayList: userDisplayList
})
the whole ponent
import React, { Component } from 'react';
import { withRouter } from "react-router-dom";
import { connect } from 'react-redux';
import * as actions from '../Actions';
class SearchPopular extends Component {
constructor(props) {
super(props);
this.state = {
list: [],
page: 1
}
this.getList = this.getList.bind(this);
}
ponentWillMount() {
//console.log(this.props.match.params.format)
this.props.fetchPopular(this.props.match.params.format, this.state.page);
console.log(this.props.popular)
console.log(this.getList());
}
getList() {
let arr = [];
if(this.props.popular){
arr = this.props.popular.map(item => {
return (
<div key={item.id} className="movie">
<img
src={`${item.poster_path}`}
//onClick={() => this.displayModal(item)}
/>
</div>)
})
}
//console.log(arr)
// this.props.updateCurrentShowList(arr);
return arr;
}
render() {
console.log(this.props.popular);
return (
<div>
</div>
);
}
}
function mapStateToProps(state) {
return {
popular: state.popular,
updatedList: state.displayList
};
}
export default withRouter(connect(mapStateToProps, actions)(SearchPopular));
In my reducer, it returns an array of objects that i got from an api. I do a console.log on the list and I'm able to see the array, but when I get access to the reducer in my react class, it shows up as an empty array, why is that so? Inside the render() function in my react file, it does print for some odd reason, but I have a function where I'm trying to render seperate divs using that data from the reducer and the array shows up empty.
getList() {
let arr = [];
if(this.props.popular){
arr = this.props.popular.map(item => {
return (
<div key={item.id} className="movie">
<img
src={`https://image.tmdb/t/p/w300${item.poster_path}`}
//onClick={() => this.displayModal(item)}
/>
</div>)
})
}
// console.log(arr)
// this.props.updateCurrentShowList(arr);
return arr;
}
I use this.props.popular from the mapstatetoprops function i have below.
import { FETCH_POPULAR, RESET_POPULAR } from "../Actions/types";
let initialList = [];
export default function(state = initialList, action){
switch(action.type){
case FETCH_POPULAR:
//return action.payload || false;
initialList = initialList.concat(...action.payload);
//console.log(initialList);
return initialList;
case RESET_POPULAR:
initialList = action.payload;
return initialList;
default:
return state;
}
}
Here the initialList is printed and works and i then return it.
This is my mapStateToProps function that i have in my other file where I want to get access to the array. I used binereducers in one of my reducers file.
function mapStateToProps(state) {
return {
popular: state.popular
};
}
Why does this.props.popular print correctly when i do it in render(), but whenever i use it anywhere else, it doesnt?
action function
export const fetchPopular = (searchTypeFormat, page) => async (dispatch) => {
let url = `https://api.themoviedb/3/discover/${searchTypeFormat}?api_key=${APIKEY}&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=${page}`;
//console.log(url);
const res = await axios.get(url);
//console.log(res.data.results)
dispatch({type: FETCH_POPULAR, payload: res.data.results});
};
my store creation
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import reducers from './Reducers/index';
import reduxThunk from 'redux-thunk';
const store = createStore(reducers, {}, applyMiddleware(reduxThunk));
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'));
I bined my reducers this way
import { bineReducers } from 'redux';
import authReducer from './authReducer';
import popularReducer from './popularReducer';
import genreListReducer from './genreListReducer';
import searchByGenreReducer from './searchByGenreReducer';
import { reducer as formReducer } from 'redux-form';
import modalReducer from './modalReducer';
import detailsReducer from './moreDetailReducer';
import userDisplayList from './userDisplayList';
export default bineReducers({
auth: authReducer,
form: formReducer,
popular: popularReducer,
genreList: genreListReducer,
searchByGenre: searchByGenreReducer,
modalData: modalReducer,
details: detailsReducer,
displayList: userDisplayList
})
the whole ponent
import React, { Component } from 'react';
import { withRouter } from "react-router-dom";
import { connect } from 'react-redux';
import * as actions from '../Actions';
class SearchPopular extends Component {
constructor(props) {
super(props);
this.state = {
list: [],
page: 1
}
this.getList = this.getList.bind(this);
}
ponentWillMount() {
//console.log(this.props.match.params.format)
this.props.fetchPopular(this.props.match.params.format, this.state.page);
console.log(this.props.popular)
console.log(this.getList());
}
getList() {
let arr = [];
if(this.props.popular){
arr = this.props.popular.map(item => {
return (
<div key={item.id} className="movie">
<img
src={`https://image.tmdb/t/p/w300${item.poster_path}`}
//onClick={() => this.displayModal(item)}
/>
</div>)
})
}
//console.log(arr)
// this.props.updateCurrentShowList(arr);
return arr;
}
render() {
console.log(this.props.popular);
return (
<div>
</div>
);
}
}
function mapStateToProps(state) {
return {
popular: state.popular,
updatedList: state.displayList
};
}
export default withRouter(connect(mapStateToProps, actions)(SearchPopular));
Share
Improve this question
edited Jul 18, 2018 at 18:35
Shubham Bhewanewala
6076 silver badges22 bronze badges
asked Jul 18, 2018 at 17:30
benoitbenoit
572 silver badges11 bronze badges
4
- Please provide your action file. – Lasithe Commented Jul 18, 2018 at 17:36
- provide store creation file – Shubham Bhewanewala Commented Jul 18, 2018 at 17:38
- @Lasitha action function added – benoit Commented Jul 18, 2018 at 17:43
- @ShubhamBhewanewala added store creation – benoit Commented Jul 18, 2018 at 17:44
2 Answers
Reset to default 5You are doing to state update in a wrong way. What you have done is it will always take empty array initially and then append into it.
case 'FETCH_POPULAR':
return [...state, ...action.payload];
Try this in your reducer.
****To your main issue You are trying to fetch store.popular but you donot have popular in store
const poseEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || pose;
const configureStore = () => {
const store = createStore(
bineReducers({
popular: Your reducer here
}),
poseEnhancer(applyMiddleware(thunk))
);
return store;
}
**** New update
I think that's the issue of function loosing the reference of this.
This is why we are using this.getList.bind(this)
in the constructor
So when we call this.getList
the function gets the reference of this and can use it. so when you are calling it directly from any other function then use this.getList.bind(this)
ponentWillMount() {
//console.log(this.props.match.params.format)
this.props.fetchPopular(this.props.match.params.format, this.state.page);
console.log(this.props.popular)
console.log(this.getList.bind(this));
}
Don't mutate variables in Redux reducers! You'll get lots of weird effects and race conditions. You want to always return fresh new objects from a reducer, unless no action matches in the default case, then return the current state
.
So firstly, don't define your initial state with a let
and then mutate it in your reducers, that's pletely wrong.
Secondly, if you want to return new state based on the previous state, as in your FETCH_POPULAR action, then use the state
argument (that's what it's for).
Rewrite like this,
export default function(state = [], action){
switch(action.type){
case FETCH_POPULAR:
return [...state, ...action.payload];
case RESET_POPULAR:
return [];
default:
return state;
}
}