I am trying to create multiple distinct Redux stores, for that am using createProvider() method in 'react-redux'.
I have installed the latest react-redux version(7.1.0), but am getting the error like "createProvider is not exported from react-redux". When i gone through the node modules, i couldn't able to find the createProvider inside the src of react-redux. Is it a version issue or did i miss something in the code. I have shared you the following code snippet as :
Provider.js
import { createProvider } from "react-redux";
export const STORE_KEY = "myComponentStore";
export const Provider = createProvider(STORE_KEY);
TestComponent.js
import React, { Component } from "react";
import { createStore } from "redux";
import Mycomponent from "./MyComponent";
import { Provider } from "./Provider";
const reducer = {};
const initialState = {
title: "multiple store"
};
const store = createStore(reducer, initialState);
class TestComponent extends Component {
render() {
return (
<Provider store={store}>
<Mycomponent />
</Provider>
);
}
}
export default TestComponent;
Mycomponent.js
import React, { Component } from "react";
import { connect } from "./Connect";
class MyComponent extends Component {
render() {
return <div>{this.props.title}</div>;
}
}
export default connect(function mapStateToProps(state) {
return {
title: state.title
};
})(MyComponent);
Seems like it is deprecated and removed since V6
Your store could be something like this:
// store.js
import { createStore } from 'redux';
import rootReducer from './root-reducer';
export default createStore(rootReducer);
And that "rootReducer" is a combination of different reducer files:
//root-reducer.js
import SomeReducers from './reducers/some-reducers';
import AnotherOne from './reducers/another-one';
const rootReducer = combineReducers({
SomeReducers,
AnotherOne,
})
export default rootReducer;
Then your store is used in the index
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from "react-redux";
import store from "./storage/store";
import App from './App';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
, document.getElementById('root')
);
But it still just one store
In documentation, it says
Don't create more than one store in an application! Instead, use
combineReducers to create a single root reducer out of many.
So one provider, one store and multiple reducers are the correct(recommended) way to use redux. Create one global provider for your application and define your reducers for that provider. You can use data selectors for your components.
For this information probably dev team decided to deprecate createProvider functionality.
I am trying to create multiple distinct Redux stores
It's fine to have multiple and distinct Redux stores in a single React application. Just don't use createProvider() for it. The two approaches to having multiple independent Redux stores are:
Use Redux sub-apps:
SubApp5.js
----------
const store = createStore(reducer)
return {
<>
<Provider store={store}>
<MyTable />
</Provider>
</>
}
With this approach multiple Redux stores will coexist.
Use multiple SPAs inside a single React application - see crisp-react. With this approach multiple Redux stores will not coexist.When you build crisp-react, it creates a React application with 2 SPAs and you can add Redux to each SPA. Then you can switch from one SPA (and its Redux store) to another SPA and its store.
Related
I am trying to access redux-store in my helper function.
My store looks like code below:
import { combineReducers, configureStore } from '#reduxjs/toolkit';
import createSagaMiddleware from 'redux-saga';
import counterReducer from 'app/Components/Counter/counterSlice';
import languageReducer from 'redux/features/app_language/language.slice';
import loginReducer, {
currentUserReducer,
currentUserIdReducer,
} from 'redux/features/app_login/loginSlice';
import rootSaga from 'redux/sagas';
import emailEditorReducer from 'redux/features/email_editor';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
const configureCustomStore = () => {
const sagaMiddleware = createSagaMiddleware();
const persistConfig = {
key: 'root',
storage,
};
const rootReducer = combineReducers({
counter: counterReducer,
app_language: languageReducer,
isAuthenticated: loginReducer,
currentUser: currentUserReducer,
currentUserId: currentUserIdReducer,
emailEditor: emailEditorReducer,
});
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = configureStore({
reducer: persistedReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false,
}).concat(sagaMiddleware),
});
const persistor = persistStore(store);
sagaMiddleware.run(rootSaga);
return { store, persistor };
};
export default configureCustomStore();
export const { store, persistor } = configureCustomStore();
console.log(store.getState(), 'State');
As you can see in the last two lines of code i am trying to console.log the state and i am getting the state as expected. But when i am importing the store in my helper function i am getting undefined. Here is how i am doing it
import storeSaga from 'redux/store/store';
const { store } = storeSaga;
Error i am getting is:
TypeError: Cannot destructure property 'store' of 'redux_store_store__WEBPACK_IMPORTED_MODULE_1__.default' as it is undefined.
I was searching for the solution i end up to this who got almost same issue, the answer is also mentioned in the next comment but honestly i didn't understand it.
I may have done something wrong in order of imports i tried all the ways i could.
For the reference you can have a look on my index.js file below:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import { Provider } from 'react-redux';
import { I18nextProvider } from 'react-i18next';
import * as serviceWorker from './serviceWorker';
import i18n from './_helpers/utils/i18n';
import storeSaga from 'redux/store/store';
const { store, persistor } = storeSaga;
import App from './app/Pages/App/App';
import { PersistGate } from 'redux-persist/integration/react';
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<PersistGate persistor={persistor}>
<I18nextProvider i18n={i18n}>
<App />
</I18nextProvider>
</PersistGate>
</Provider>
</React.StrictMode>,
document.getElementById('root'),
);
serviceWorker.unregister();
I appreciate your help so much. Thanks in advance.
In
export default configureCustomStore();
export const { store, persistor } = configureCustomStore();
you are creating two independent stores.
Also, when importing you import the first of them and call it storeSaga even though this has nothing to do with a saga at all.
Kick the default export out and do import { store } from 'redux/store/store'; everywhere.
That makes it much clearer what you are doing.
If you still have errors after that, you have a circular import somewhere that you need to resolve, meaning that this file imports from a file that also imports this file (maybe with a third file in-between), forming a circle. JavaScript cannot work with that.
You can Inject Store to any file outside react component like this example:
File axios:
let store
export const injectStore = _store => {
store = _store
}
axiosInstance.interceptors.request.use(config => {
config.headers.authorization = store.getState().auth.token
return config
})
File index (root of your project)
import store from './app/store'
import {injectStore} from './common/axios'
injectStore(store)
You can also read about this here:
https://redux.js.org/faq/code-structure#how-can-i-use-the-redux-store-in-non-component-files
I'm setting up ContextApi for the first time in a production app, hoping to replace our current handling of our app configs with it. I've followed the official docs and consulted with similar issues other people are experiencing with the API, and gotten it to a point where I am able to correctly the config when I do Config.Consumer and a callback in render functions. However, I cannot get this.context to return anything other than an empty object.
Ideally, I would use this.context in lifecycle methods and to avoid callback hell, so help would be appreciated. I've double checked my React version and that I'm setting the contextType. Below is a representation of the code
config.js
import { createContext } from "react";
export default createContext();
index.js
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { Router, browserHistory } from "react-router";
import { syncHistoryWithStore } from "react-router-redux";
import Config from "../somePath/config";
// more imports
function init() {
const config = getConfig();
const routes = getRoutes(config);
const history = syncHistoryWithStore(browserHistory, appStore);
ReactDOM.render(
<Provider store={appStore}>
<Config.Provider value={config}>
<Router history={history} routes={routes} />
</Config.Provider>
</Provider>,
document.getElementById("app")
);
}
init();
someNestedComponent.js
import React, { Component } from "react";
import { connect } from "react-redux";
import Config from "../somePath/config";
#connect(
state => ({
someState: state.someState,
})
)
class someNestedComponent extends Component {
componentDidMount() {
console.log(this.context);
}
render() {
return (...someJSX);
}
}
someNestedComponent.contextType = Config;
export default someNestedComponent;
Currently running on:
React 16.8.6 (hopi to see error messages about circuitous code but
didn't get any warnings)
React-DOM 16.7.0
React-Redux 6.0.1
The problem is that someNestedComponent doesn't refer to the class where this.context is used:
someNestedComponent.contextType = Config;
It refers to functional component that wraps original class because it was decorated with #connect decorator, it is syntactic sugar for:
const someNestedComponent = connect(...)(class someNestedComponent extends Component {
...
});
someNestedComponent.contextType = Config;
Instead, it should be:
#connect(...)
class someNestedComponent extends Component {
static contextType = Config;
componentDidMount() {
console.log(this.context);
}
...
}
There are no callback hell problems with context API; this is conveniently solved with same higher-order component pattern as used in React Redux and can also benefit from decorator syntax:
const withConfig = Comp => props => (
<Config.Consumer>{config => <Comp config={config} {...props} />}</Config.Consumer>
);
#connect(...)
#withConfig
class someNestedComponent extends Component {
componentDidMount() {
console.log(this.props.config);
}
...
}
You didn't use a consumer to get the values
ref: https://reactjs.org/docs/context.html#contextconsumer
I'm trying to export a redux project as a node_module that has an index.js shown below (simplified):
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import promiseMiddleware from 'redux-promise-middleware';
import App from './App.jsx';
const middlewares = [thunk.withExtraArgument(), promiseMiddleware()];
const middlewareEnhancer = applyMiddleware(...middlewares);
const preloadedState = {};
const store = createStore(
rootReducer,
preloadedState,
middlewareEnhancer
);
const ExampleModule = (props) => {
return (
<Provider store={store}>
<App />
</Provider>
);
};
export default ExampleModule;
In my main application:
...
import ExampleModule from 'example-module';
class Application extends React.Component {
render() {
return <ExampleModule />;
}
}
function mapStateToProps({ state }) {
return {
state: state
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(require('..').actions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Application);
This throws an error:
bundle.js:349 Uncaught Invariant Violation: Could not find "store" in either the context or props of "Connect(App)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(App)"
I'm assuming it's because this essentially creates nested <Providers> which is against Redux's methodology of one store.
My question would be what would be the best way to go about publishing a node_module that has a redux store in it?
Found the answer here:
https://redux.js.org/recipes/isolating-redux-sub-apps
It keeps the store local to the component.
So technically I have 2 components, I dispatch event from 1st, I want detect this change in 2nd.
I did everything as in Redux docs about Store subscribing : https://redux.js.org/api/store#subscribe. Unfortunatelly, it's not working for me.
This is my 1st react project.
(vue/x is better :] )
import React, {Component} from 'react';
import reducers from '../../reducers'
import { Dropdown } from 'semantic-ui-react'
import {translate} from "../../actions";
import createStore from "../../createStore";
const store = createStore(reducers)
class Component1 extends Component {
componentDidMount() {
store.subscribe(() => console.log(1));
}
updateTexts(lang) {
store.dispatch(translate(lang));
}
render() {
this.dropdown = <Dropdown
onChange={this.updateTexts}
/>
return (
<div className={"lang-switcher"}>
<div className={"select-lang"}>
{this.dropdown}
</div>
</div>
);
}
}
export default Component1
import React, {Component} from 'react';
import axios from 'axios';
import {Animate} from 'react-animate-mount';
import createStore from "../../createStore";
import reducers from "../../reducers";
const store = createStore(reducers);
export default class Component2 extends Component {
componentDidMount() {
store.subscribe(console.log(2));
}
render() {
return (
<div className="box">
{Something}
</div>
);
}
}
I want that Component2 will detect state change done by Component1.
Reducer is working correctly, updates state after dispatching.
If you're using React, you should be using the React-Redux library to handle interacting with the store.
That said, it also looks like you're creating two different store instances, one in each component file. So, Component 2 doesn't know about the store instance in Component 1's file.
Please create a script Store.js, and import for each component.
When you use export, that will create a singleton from your export const:
Store.js
import reducers from '../../reducers'
import createStore from "../../createStore"
export default createStore(reducers)
and use as:
import store from "./Store";
/* REMOVE const store = createStore(reducers); */
I am trying to get the Redux state that is stored to be set to a component's props. My components implementation is below
import React, { Component } from 'react';
import {connect} from 'react-redux';
class Characters extends Component {
render() {
console.log('props', this.props);
return(
<div>
<h4>Characters</h4>
</div>
)
}
}
const mapStateToProps = state => {
return {
characters: state.characters
}
}
export default connect()(Characters);
I have my reducers set up like following. Now my console.log() is printing out a dispatch object not the props. I am not sure what else I need to do to set the component's props. I have tested my reducers and they seem to work fine. I am having trouble setting the component's props to the redux store. Below is my main reducer.
import { combineReducers } from 'redux';
import characters from './characters_reducer';
import heroes from './heroes_reducer';
const rootReducers = combineReducers({
characters,
heroes
});
export default rootReducers;
Not sure what am I doing wrong here. A little help and a hinge to the right direction would help a lot. Thanks :)
You forgot to pass mapStateToProps to connect:
export default connect(mapStateToProps)(Characters);
You need to pass in your actionCreators and your mapStateToProps function to the connect function in order to be able to access the store from the component:
export default connect(mapStateToProps)(Characters);