2
votes

I am embarking on a project which involves using typescript/react/redux together with golden layout. I'm having some issues getting Redux's Provider Store working with react. I've done some previous searching, and came across the following question:

How to use Redux's Provider with React

I've been following the advice given in the answers to that question, to write my own code, as seen below:

import React from 'react';
import GoldenLayout from 'golden-layout';
import {Provider} from 'react-redux';
import Default from '../modules/m-Default/Default';
import PropTypes from 'prop-types';

interface GoldenLayoutWrapperProps {}

interface GoldenLayoutWrapperState {}

class GoldenLayoutWrapper extends React.Component <GoldenLayoutWrapperProps, GoldenLayoutWrapperState> {

    public layout;
    static contextTypes = {
        store: PropTypes.object.isRequired
    };

    private wrapComponent(Component, store) {
        class Wrapped extends React.Component {
            render () {
                return (
                    <Provider store={store}>
                        <Component {...this.props}/>
                    </Provider>
                )
            }
        }
        return Wrapped;
    }

    componentDidMount() {
        var layout = new GoldenLayout(this.layoutConfig, this.layout);
        layout.registerComponent('Default', this.wrapComponent(Default, this.context.store));
    }

    render() {
        return (
            <div className="golden-layout-wrapper" ref={input => this.layout = input}/>
        )
    }

    private layoutConfig = {}
}

export default GoldenLayoutWrapper;

You can see, basically what my class is doing, is setting up a golden layout interface by registering react components with golden layout. So that this works with redux, I've defined the wrapComponent function to return the react component to be registered, but wrapped in a Redux Provider.

I followed the pattern outlined in the question I linked above, using context.store and store: PropTypes.object.isRequired

However, when I run the application, I get the following error from my browser console:

Store does not have a valid reducer. Make sure the argument passed to combineReducers is an object whose values are reducers.

Under this, in my browser console, I get the error:

Uncaught TypeError: Cannot read property 'object' of undefined at new GoldenLayoutWrapper

Still being new to Redux, I'm not 100% sure what this means, but it seems to me that store is undefined, and hence does not have a valid reducer. But, according to the question I liked above, this should work.

What could be the problem here?

Here's my code for the root app and root reducer:

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Store, createStore } from 'redux';
import { Provider } from 'react-redux';
import GoldenlayoutWrapper from './main/GoldenLayoutWrapper';

import rootReducer from './main/rootReducer';

const initialState = {};
const store: Store<any> = createStore(rootReducer, initialState);

ReactDOM.render(
    <Provider store={store}>
        <GoldenlayoutWrapper/>
    </Provider>,
    document.getElementById('root')
);

I've left rootReducers empty so that developers in the future can add their code to it:

import { combineReducers } from 'redux';

// Import your reducers here

const rootReducer = combineReducers({
    // Add your reducers here
});

export default rootReducer;

I've also tried the following:

In my root app, defining store as const store:<any> = createStore(state=>state, initialState) rather than const store:<any> = createStore(rootReducer, initialState)

And in my root reducer, trying to specify a blank reducer: blank: function(state, action) {if (state == null) state = [] return state;}

But neither of these worked. I got the same error as above.

1
how is your store looks like? Is it defined by you or you assume that it should come from somewhere provided to you?:)Lukasz 'Severiaan' Grela
Add the code where you call createStore. The error means that when creating the store you did not provide a reducer for it.trixn
@Lukasz'Severiaan'Grela I've edited my post with that informationJavascriptLoser
@trixn I've added that informationJavascriptLoser

1 Answers

1
votes

Following is base use of the Redux, see if your code has all ingredients required:

import { createStore, combineReducers } from 'redux';


// ACTIONS
const SAMPLE_ACTION_TYPE = 'gd:SAMPLE_ACTION_TYPE';
const sampleAction = (payload) => ({
    type: SAMPLE_ACTION_TYPE,
    payload
});


// REDUCER
const sampleReducer = (state={}, action) => {
    return state;
};
const sampleTwoReducer = (state=[],action) => {
    return state;
}

// STORE
const store = createStore(
    combineReducers({
        sample:sampleReducer,
        sampleTwo:sampleTwoReducer
    })
);

// SUBSCRIBE
store.subscribe(() => {
    console.log("STORE STATE", store.getState());
});

// DISPATCH SOME ACTIONS
store.dispatch(sampleAction());

Above creates following output:

STORE STATE 
{sample: {…}, sampleTwo: Array(0)}
sample:
__proto__:Object
sampleTwo:Array(0) length:0
__proto__:Array(0)
__proto__:Object

but changing the code to:

// STORE
const store = createStore(
    combineReducers({
        sample:null,
        sampleTwo:null
    })
);

produces the same error, check what is passed to combineReducer

** EDIT ** To provide the store to the components you do two things, first set the Provider

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';


ReactDOM.render(
    <Provider store={store}>
        <MyApp />
    </Provider >, document.getElementById('app'));

then in your component you are calling High Order Functions connect this will connect your component to the store, detailed description in the docs

import { connect } from 'react-redux';
import React from 'react';
class MyComponent extends React.Component {
    notMapped = (payload) => {
        this.props.dispatch(sampleAction(payload));
    }
    render() {
        const {sample, mappedFunction} = this.props;
        return (
            <div>
                <p>{sample}</p>
                <button onClick={() => {
                    mappedFunction("test");
                }}>Call mapped Function</button>
                <button onClick={() => {
                    this.notMapped("test");
                }}>Call NOT mapped Function</button>
            </div>
        );
    }
}
const mapDispatchToProps = (dispatch) => ({
    mappedFunction: (payload) => dispatch(sampleAction(payload))
});
const mapStateToProps = (state) => ({
    sample: state.sample
});
export default connect(mapStateToProps,mapDispatchToProps)(MyComponent);// connect