DMZ
DMZ

Reputation: 185

React & Redux : cannot read property of undefined

I am trying to write an application in react, and I faced the issue of having to manage the state for so many components, so I wanted to use redux. From what I read, Redux doesn't work on the root component, so I created an intermediate component named it "wrapper" that will wrap everything and manage the state of my application. So now I have my original root component and a wrapper component. I am trying to get some props in this wrapper component using redux, but the props aren't populated for some reason and I can't figure it out.

Here's my code:

import * as React from 'react'
import { Provider } from "react-redux";
import { connect } from "react-redux";
//SPFX references
import { DefaultButton, IButtonProps } from 'office-ui-fabric-react/lib/Button';
import { Panel, PanelType } from 'office-ui-fabric-react/lib/Panel';

//custom references
import { IRootProps } from '../props/IRootProps'
import { IRootState } from '../states/IRootState'
import { ITile, ITileHeader, ITileItem } from '../props/ITileStructure'
import store from '../main/store'
import { openPanel, closePanel } from '../actions/panelAction'

//Styles references
import rootStyles from '../styles/RooComponent.module.scss'
import commonStyles from '../styles/common.module.scss'

//Components references
import PanelComponent from './PanelComponent'
import FooterComponent from './FooterComponent'
import DiscardChangesDialogComponent from './DiscardChangesDialogComponent'
import BladesRootComponent from './BladesRootComponent'
import TilesListComponent from './TilesListComponent'

class RootComponent extends React.Component<{}, {}>{
    tiles: ITile[] = [];

    constructor(props) {
        super(props);
    }

    render(): JSX.Element {
        return (
            <Provider store={store}>
                <AppWrapper />
            </Provider>

        )
    }
}

class AppWrapper extends React.Component<IRootProps, IRootState>{
    tiles: ITile[] = [];

    constructor(props: IRootProps) {
        super(props);

        if (this.tiles.length === 0) {
            for (let i = 0; i < 200; i++) {
                this.tiles.push({
                    key: i,
                    tileName: 'tile ' + i,
                    tileImageUrl: 'image url ' + i,
                    tileOrder: i,
                    headers: []
                });
            }
        }
    }
    render(): JSX.Element {
        return (
            <div className="RootComponent">
                <DefaultButton onClick={() => this.props.panelProps.openPanel()} className={commonStyles.defaultButton} >Configure Tiles</DefaultButton>
                <Panel
                    isOpen={this.props.panelProps.isPanelOpen}
                    // tslint:disable-next-line:jsx-no-lambda
                    onDismiss={() => {
                        this.setState({ showPanel: false })
                    }}
                    type={PanelType.extraLarge}
                    headerText="Tiles configuration"
                    hasCloseButton={false}>
                    <PanelComponent />
                </Panel>
            </div>
        )
    }
}

let mapStateToProps = (state) => {
    return {
        panelProps: state.panel,
    };
};

let mapDispatchToProps = (dispatch) => {
    return {
        openPanel: () => {
            dispatch(openPanel());
        }
    };
};

connect(mapStateToProps, mapDispatchToProps)(AppWrapper);
export default RootComponent; 

This is the code for the ../main/store:

import {createStore, combineReducers} from 'redux';
import {Store} from 'redux';

import panel from "../reducers/panelReducer";
let store =  createStore(
    combineReducers({
        panel
    }),
    {}
);

export default store; 

and ../reducers/panelReduce looks like this:

//(state, action) => 
const panelReducer = (state = {
    isPanelOpen: false
}, action) => {
    switch (action.type) {
        case "OPEN_PANEL":
            state = {
                ...state,
                isPanelOpen: true
            };
            break;
        case "CLOSE_PANEL":
            state = {
                ...state,
                isPanelOpen: false
            };
            break;
    }
    return state;
};

export default panelReducer;

However, the code is getting executed and at the line : <Panel isOpen={this.props.panelProps.isPanelOpen} it's telling me cannot access isPanelOpen of undefined. For some reason Redux isn't connected the right way.

P.S I am still a beginner in Redux so I might be doing things wrong, if anyone can point me to the right direction.

Upvotes: 0

Views: 815

Answers (1)

Olivier Boiss&#233;
Olivier Boiss&#233;

Reputation: 18113

In fact you didn't use the connected AppWrapper component inside your RootComponent

You can create a constant AppWrapperConnected : const AppWrapperConnected = connect(mapStateToProps, mapDispatchToProps)(AppWrapper); and use <AppWrapperConnected/> inside the RootComponent

Moreover I think it will be easier to maintain if you split RooComponent and AppWrapper in two separate files

Another advice is to rewrite mapStateToProps like this

let mapStateToProps = (state) => state.panel

so you can directly use this.props.isPanelOpen, I think panelProps is useless here

Upvotes: 2

Related Questions