2

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.org/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 combinereducers 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.org/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 combined my reducers this way

import { combineReducers } 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 combineReducers({
    auth: authReducer,
    form: formReducer,
    popular: popularReducer,
    genreList: genreListReducer,
    searchByGenre: searchByGenreReducer,
    modalData: modalReducer,
    details: detailsReducer,
    displayList: userDisplayList
})

the whole component

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);
    }
    componentWillMount() {
        //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.org/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));
4
  • Please provide your action file. Commented Jul 18, 2018 at 17:36
  • provide store creation file Commented Jul 18, 2018 at 17:38
  • @Lasitha action function added Commented Jul 18, 2018 at 17:43
  • @ShubhamBhewanewala added store creation Commented Jul 18, 2018 at 17:44

2 Answers 2

5

You 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 composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

const configureStore = () => {
    const store = createStore(
        combineReducers({
            popular: Your reducer here
        }),
        composeEnhancer(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)

componentWillMount() {
        //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));
    }
Sign up to request clarification or add additional context in comments.

11 Comments

is this coming from combinereducer import reducers from './Reducers/index';
Why does this.props.popular print correctly when i do it in render(), but whenever i use it anywhere else, it doesnt? --- are you trying to access it inside class or outside class? it will be good if you can post entire component
thats a completely correct output since the array you are returning is the array of elements
because initialy your state.popular donot have anything so an empty array. as this a lifecycle method it is called before rendering so i believe till then props is also not filled. And using this method is unsafe as per new update in react
oh, ok i think im starting to get it, ill play around with it @shubham bhewanewala
|
2

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 completely 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;
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.