Is it possible to ES6 import a React component from node_modules that depends on a Context Provider (like react-redux 6.0) without the Provider Context being exported by that module?
For example, the implementation of the import would wrap the imported component with its own Provider.
import App from 'app-package'
import { Provider } from 'react-redux'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('example'),
);
this works in react-redux 5.1.1 but not in 6.0.0, I assume because 6.0.0 is now using the React Context API. The problem may also be webpack related.
Found this https://github.com/facebook/react/issues/13336.
It seems that <App/> must have its own Provider Context since it is outside of the imperative boundary.
To share store with <App/>, one can use ReactReduxContext.Consumer and pass store as a prop to it (via a component wrapper)
Related
We are building a Storybook UI library from our existing code base. The code wasn't written with component driven development in mind. There are many instances where a component renders descendants that are connected to the Redux store.
E.g., Parent (connected) -> Child (unconnected) -> Grandchild (connected)
Now if I'm building a story for Parent, I understand how to pass hard-coded data as a prop to an immediate child component in order to avoid Redux all together. However, I can't figure out how to do this when the connected component is more deeply nested.
Ideally I don't want to have to use Redux at all for stories, but even if I do initialize a Redux store and wrap the parent component in a Provider as described here, would this even work to connect the grandchild component?
Any ideas would be helpful.
When using storybook you can add a Decorator for all stories (see link for most updated API).
It is common to wrap your stories with the state manager store provider in order to not break the story avoiding "adding a store for each story".
// # config.js
import { configure, addDecorator } from '#storybook/react';
import React from 'react';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import rootReducer from 'reducers/root.reducer';
const store = createStore(rootReducer);
addDecorator(S => (
<Provider store={store}>
<S />
</Provider>
));
configure(require.context('../src', true, /\.stories\.js$/), module);
Note that you can avoid connecting all your components with redux-hooks which in addition removes all the boilerplate code of redux.
React Redux now offers a set of hook APIs as an alternative to the existing connect() Higher Order Component. These APIs allow you to subscribe to the Redux store and dispatch actions, without having to wrap your components in connect().
If you want to solve the problem within your story file (and just fetch your store), use decorator like this:
import React from "react";
import { Provider } from 'react-redux'
import Parent from "./Parent";
import { store } from "../../../redux/store";
export default = {
title: "pages/Parent",
component: Parent,
decorators : [
(Story) => (<Provider store={store}><Story/></Provider>)
]
};
Sidenote, if this gives you the error useNavigate() may be used only in the context of a <Router> component., then you may need <MemoryRouter><Provider store={store}><Story/></Provider></MemoryRouter> (import {MemoryRouter} from 'react-router-dom')
I am using react, styleguidist and redux. I am trying to document different app states depending on redux store state, but the playground the .md file creates only uses one store. The .md file looks like this:
import * as actions from './redux/actions';
import { Provider } from 'react-redux'
import store from './redux/store';
(
<Provider store={store}>
<App />
</Provider>
)
in the same .md file there is another .js block, as the example [here]:(https://react-styleguidist.js.org/docs/documenting.html#usage-examples-and-readme-files)
the block is :
import * as actions from './redux/actions';
import { Provider } from 'react-redux'
import store from './redux/store';
// the live sample in the styleguidist server updates correctly when dispatching the action
store.dispatch(actions.showConnection(true));
//THE PROBLEM IS THAT THE OTHER SAMPLE (THE ONE ABOVE) ALSO UPDATES) so there is one store for the entire sample
//¿How can I dispatch actions and only update the sample inside each ``` js code?
//¿How can I mock the redux store inside each js block sample in the .md file ?
(
<Provider store={store}>
<App />
</Provider>
)
any help will be appreciated.
The best practice for using Redux in React application is wrapping the component in a 'Provider' component:
const rootElement = document.getElementById('root')
ReactDOM.render(
<Provider store={store}>
<TodoApp />
</Provider>,
rootElement
)
You can see it in React-Redux documentation: https://react-redux.js.org/introduction/basic-tutorial.
What is the benefit we get from this attitude?
Why not just importing the 'store' inside the 'ToDoApp' component and access 'store' as an imported variable? For example:
import { store } from './store';
class TodoApp extends Component {
constructor(props) {
super(props);
console.log('constructor')
}
render() {
console.log(store.getState());
}
}
The actual point that is happening in the redux, when we are calling the provider: that it is having the store of all the states and the provider does the job to connect the component with the redux or simply you can say that the provider does the job to connect your app with the redux as the author of the redux has not only to design the library for a single framework, it would have so many uses on different platforms, the store is having two things inside (reducers and state) and all the states get an outer layer of provider which connects the app with the redux library.
This is very important to the way react-redux works.
When you use connect over your component, it attempts to get the store from the Provider you set, using React's context mechanism.
It is highly unlikely that you will use Redux in React without using connect, so I would advise that you keep it there.
I'm trying to upgrade to Redux V6 but am confused on how to use the createContext function and it's necessity. I know my store is created successfully, but when I try to run my app I get
Could not find "store" in the context of "Connect(ConfiguredApp)".
Either wrap the root component in a , or pass a custom React
context provider to and the corresponding React context
consumer to Connect(ConfiguredApp) in connect options.
Which tells me that my provider is not properly passing down the store for connect to grab. What am I doing wrong? Thanks!
import 'babel-polyfill';
import React from 'react';
import {render} from 'react-dom';
import {Provider} from 'react-redux';
import {ConnectedRouter} from 'connected-react-router';
import {history, store} from './store/store';
import Routes from './routes';
const customContext = React.createContext(null);
render(
<Provider store={store} context={customContext}>
<ConnectedRouter history={history} context={customContext}>
<Routes />
</ConnectedRouter>
</Provider>, document.getElementById('app'),
);
You almost definitely shouldn't be creating and passing in a custom context instance. That's only if, for some very specific reason, you want to use a context instance other than the default one that React-Redux already uses internally. (A hypothetical reason to do this would be if you are using one store for your whole app component tree, but there's a specific subtree that needs to receive data from a different store instead.)
If you actually wanted to use your own custom context instance, then you would need to pass the same context instance to both <Provider> and each connected component in the app that needs to receive data from that <Provider>.
Looking at the connected-react-router docs, they do claim that in CRR version 6, you can pass a context instance to <ConnectedRouter>, but that shouldn't be necessary here.
More specifically, if you look at the error message, it says the problem is in Connect(ConfiguredApp). So, it's your own connected component that is saying there's a context mismatch.
Ultimately, the answer here is to delete the context arguments from both <Provider> and <ConnectedRouter>. You don't need them.
I'm new to react and redux, I started out my project with the react-router-dom, now I'm looking to add Redux in the mix.
Should I keep using react-router-dom or just install react-router-redux? or both?
Does the Route component work the same? I'm a bit confused as what the use of react-router-dom would be?
Do I switch all the Link components to an a element that dispatches an action to the store?
Thanks for any help.
Generally when using Redux, it's recommended to use react-router-redux, which encapsulates react-router.
Only when you initialize your app, you should pass a couple of things from react-router to Redux, which are Router and browserHistory.
In app.js:
import { Router, browserHistory } from 'react-router';
import { syncHistoryWithStore } from 'react-router-redux';
const initialState = {};
const store = configureStore(initialState, browserHistory);
const history = syncHistoryWithStore(browserHistory, store);
const render = () => {
ReactDOM.render(
<Provider store={store}>
<Router
history={history}
routes={rootRoute}
/>
</Provider>,
document.getElementById('app')
);
};
You provide the Router element of react as a child of Redux's Provider element, and you also provide to the Router element the Redux wrapper for React's browserHistory.
In the rest of your project, you only need to use react-router-redux, rather than calling React directly.
react-router-dom BTW is just another layer of simplification in React 4 which encapsulates react-router, but in case your project architecture is Redux, than you need to work according to the rules of Redux and therefore only use react-router-redux in order to pass through the store in each action (except for the aforementioned initialization).
i will recommend you to use react-router-redux.
using react-router-redux you can connect your router state with your Redux Store so that you can Interact with the Router with the same API you use to interact with the rest of your app state.
Use react-router-redux.
With react-router-redux the location state is kept in your redux store as plain object. This means you can inject location information with connect as you would any other state.