Warning: Failed propType: Required prop `dimension

2019-04-10 11:38发布

问题:

I have the following Redux+React component

import {PropTypes, React, Component} from 'react';
import Select from 'react-select';

class DimensionPicker extends Component {
    componentDidMount() {
        const {onLoad} = this.props;
        onLoad();
    }
    render() {
        const {onChange, attributeList, currentAttribute} = this.props;
        return (
            <div>
                <Select value={currentAttribute} options={attributeList} onChange={onChange} />
            </div>
        )       
    }
}

DimensionPicker.propTypes = {
    dimensionName: PropTypes.string.isRequired,
    onChange: PropTypes.func.isRequired,
    attributeList: PropTypes.arrayOf(PropTypes.shape({
        value: PropTypes.string.isRequired,
        label: PropTypes.string.isRequired
    }).isRequired).isRequired,
    currentAttribute: PropTypes.string.isRequired
}

export default DimensionPicker;

and the following container component

import React from 'react';
import DimensionPickerActions from '../actions/DimensionPickerActions';
import {connect} from 'react-redux';
import DimensionPicker from './controls/DimensionPicker.jsx';

const mapStateToProps = (state) => {
    return {
        dimensionName: state.dimensionName,
        attributeList: state.attributeList,
        currentAttribute: state.currentAttribute
    }
}

const mapDispatchToProps = (state) => {
    return {
        onChange: (newValue) => {
            dispatch(updateAttributeSelection(newValue));
        },
        onLoad: () => {
            dispatch(fetchDimensionAttributes(state.dimensionName));
        }
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(DimensionPicker);

I also have a reducer which populates the initial state

// define the state tree for the dimenion picker.
const initialState = {
    dimenisionName: '',
    isLoading :'false',
    error : '',
    currentAttribute: '',
    attributeList: []
}

function dimensionPickerReducer(state = initialState, action) {

    switch(action.type) {
        case ATTRIBUTE_SELECTION_CHANGED: 
            return Object.assign({}, state, {currentAttribute: action.data});
            break;
        case REQUEST_DIMENSION_ATTRIBUTES:
            return Object.assign({}, state, {isLoading: 'true', error: ''})
            break;
        case DIMENSION_ATTRIBUTES_RECEIVED:
            return Object.assign({}, state, {attributeList: action.data, isLoading: 'false', error: action.error});
            break;
        case SET_DIMENSION_NAME:
            return Object.assign({}, state, {dimensionName: action.data})
            break;
        default:
            return state;
            break;
    }
}

export default dimensionPickerReducer;

I build my state store like this

import React from 'react';
import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import DataTableReducer from './reducers/DataTableReducer';
import DimensionPickerReducer from './reducers/DimensionPickerReducer';

const combinedReducer = combineReducers({
    dataTable: DataTableReducer,
    dimensionPicker: DimensionPickerReducer
});
export default applyMiddleware(thunk)(createStore)(combinedReducer);

I load the component like

import React from 'react';
import DimensionPicker from '../containers/DimensionPickerContainer';

const App = () => (
    <div>
    <DimensionPicker dimensionName="Genre"/>
    </div>
    )

export default App;

and finally here is how I load my App

import React from 'react';
import {render} from 'react-dom';
import {Provider} from 'react-redux';
import App from './Reports/App.jsx';
import MovieLensAppStore from './stores/MovieLensAppStore';

render (
    <Provider store={MovieLensAppStore}>
        <App />
    </Provider>,
    document.getElementById('container')
    )

My expectation was that

  1. the reducer will initialize state
  2. the container component will map that state to props using the 2 methods in the container component
  3. finally when the component loads, it will have the state and the dispatch methods available to it.

but that does not happen. instead I get a warning like

Warning: Failed propType: Required prop `dimensionName` was not specified in `DimensionPicker`. Check the render method of `Connect(DimensionPicker)`.

I have published my entire code base here

https://github.com/abhitechdojo/MovieLensReact

回答1:

You provide the "initial state" as a default parameter to your reducer, but this is only used as the default state for that reducer when it's actually invoked. Since you have not dispatched any actions yet, the initial state depends on the value you provided to createStore, presumably in MovieLensAppStore.

I don't know how you create your store, but this should work, for example:

createStore(
    combineReducers({
        dimensionPickerReducer
    }),
    {
        dimensionPicker: {
            dimenisionName: '',
            isLoading :'false',
            error : '',
            currentAttribute: '',
            attributeList: []
        }
    }
)


回答2:

The problem was resolved. the troublesome piece here is that the combineReducers is not initializing the state properly, and that is why the container control is saying that the props have not been specified (because the state is empty).

The solution is documented here

combineReducers causes code to break