React-Redux ailed propType - value undefined

I am trying to use propTypes to help typeCheck/validate data in my app. The data comes through fine but I get a warning; Warning: Failed prop type: The propallSubjectSubcategoriesis marked as required inOddMainPage, but its value isundefined`.

I've read through some of the other responses but I haven't yet fpound a solution to fix my issues.

COMPONENT

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

        this.state = {
            categoryTables: [],
            tableColumns: [],

            gettingColumns: false,
            selectedCategory: "",
            selectedTable: ""
        }
    }

    componentDidMount() {
        this.props.dispatch(getAllSubjectSubCategories());
    }

  //more code to render some data
}}

OddMainPage.propTypes = {
    allSubjectSubcategories: PropTypes.array.isRequired,
    subCategoryTables: PropTypes.array,
    tableColumns: PropTypes.array
}

const mapStateToProps = state => ({
    allSubjectSubcategories: state.allSubjectSubcategories.allSubjectSubcategories,
    subCategoryTables: state.subCategoryTables.subCategoryTables,
    tableColumns: state.tableColumns.tableColumns
})

export default connect(mapStateToProps)(OddMainPage);

REDUCER

const initialState = {
    subCategoryTables: [],
    allSubjectCategories: [],
    allSubjectSubcategories: []
}

export const getAllSubjectSubcategoriesReducer = (state = initialState.allSubjectSubcategories, action) => {
    switch (action.type) {
        case "GET_ALL_SUBJECT_SUBCATEGORIES":
            return {
                ...state,
                allSubjectSubcategories: action.allSubCats
            }
        default:
            return initialState.allSubjectSubcategories
    }
}

I also tried setting default state to default: return state but get the same results.

STORE

import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import rootReducer from "../reducers";

const initialState = {};
const middleware = [thunk];

let store;

if (window.navigator.userAgent.includes("Chrome")) {
  store = createStore(
    rootReducer,
    initialState,
    compose(
      applyMiddleware(...middleware),
      window.__REDUX_DEVTOOLS_EXTENSION__ &&
      window.__REDUX_DEVTOOLS_EXTENSION__()
    )
  );
} else {
  store = createStore(
    rootReducer,
    initialState,
    compose(applyMiddleware(...middleware))
  );
}

export default store;

Upvotes: 0

Views: 80

Answers (1)

azundo
azundo

Reputation: 6052

It looks like you're changing the return type of your reducer form an array to an object on the GET_ALL_SUBJECT_SUBCATEGORIES action.

Looking at initialState for getAllSubcategoriesReducer, you can see the value is an array. However the return value for the GET_ALL_SUBJECT_SUBCATEGORIES branch is an object. You'll need to standardize on one or the other.

Since the initial state of the reducer is just an empty array, the value of state.allSubjectSubcategories in mapStateToProps will be that empty array. So when you call allSubjectSubcategories: state.allSubjectSubcategories.allSubjectSubcategories, you get undefined.

If you want to keep the nested version you will need to change initialState (and fix the default case of the reducer):

// NOTE: I've nested all of your sub-reducer keys to match your `mapStateToProps`.
const initialState = {
    subCategoryTables: {
      subCategoryTables: [],
    },
    allSubjectCategories: {
      allSubjectCategories: [],
    },
    allSubjectSubcategories: {
      allSubjectSubcategories: [],
    }
}


export const getAllSubjectSubcategoriesReducer = (state = initialState.allSubjectSubcategories, action) => {
    switch (action.type) {
        case "GET_ALL_SUBJECT_SUBCATEGORIES":
            return {
                ...state,
                allSubjectSubcategories: action.allSubCats
            }
        // return `state` here or you will keep reseting your reducer state
        default:
            return state
    }
}

If you want to keep the reducer as an array like the initial state you will need to update your reducer and your mapStateToProps:

export const getAllSubjectSubcategoriesReducer = (state = initialState.allSubjectSubcategories, action) => {
    switch (action.type) {
        case "GET_ALL_SUBJECT_SUBCATEGORIES":
            // assuming action.allSubCats is an array, we want to replace this entire reducer state with the new array
            return action.allSubCats
        // return `state` here or you will keep reseting your reducer state
        default:
            return state
    }
}

Now that the reducer above always returns an array you can update your mapStateToProps to remove the extra key being introduced before:

const mapStateToProps = state => ({
    allSubjectSubcategories: state.allSubjectSubcategories,  // note the change here
    // you probably want these to be updated as well with similar changes to your other reducers
    subCategoryTables: state.subCategoryTables,
    tableColumns: state.tableColumns
})

Upvotes: 1

Related Questions