最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Why is my reducer returning an empty array in reactredux? - Stack Overflow

programmeradmin0浏览0评论

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
Add a ment  | 

2 Answers 2

Reset to default 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 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;
  }
}
发布评论

评论列表(0)

  1. 暂无评论