How to use React Context API with redux? - javascript

I have a react redux application that I want to use React Context API for one of the things I'm trying to do
so I have this
<Provider value="something" store={store}>
// stuff in here
</Provider>
which works fine but then I tried to add context
const AppTypeContext = React.createContext('someContext' as any)
<AppTypeContext.Provider value="myValue" store={store}>
// stuff in here
</AppTypeContext.Provider>
then my app threw an error saying Property 'store' does not exist on type 'IntrinsicAttributes & ProviderProps<any>'. TS2322
how do I fix this? and what provider do I need to use?

The app can have multiple contexts.
Just separate properly the provided values:
store is to be provided via Provider
"something" should be provided by your AppTypeContext.Provider.
<Provider store={store}> // redux provider
<AppTypeContext.Provider value="something"> // your provider
{children}
</AppTypeContext.Provider>
</Provider>

Related

how to implement the new react-redux v6.0.0

i was trying to migrate react-redux v5.X.X to v6.0.0 and there dosent seem to be any documentation for it.
i am using following versions :
"react": "^16.4.2"
"redux": "^4.0.0"
"react-redux": "^6.0.0"
the official change log says.
Passing store as a prop to a connected component is no longer supported. Instead, you may pass a custom context={MyContext} prop to both and . You may also pass {context : MyContext} as an option to connect.
link is here
here is my root index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import { configureStore, history } from './Store';
import App from './App.hot';
import 'antd/dist/antd.min.css';
const reduxStore = configureStore();
ReactDOM.render(<App store={reduxStore} history={history} />, document.getElementById('root'));
here is my app.jsx (root component)
import React from 'react';
import PropTypes from 'prop-types';
import { Provider, connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { ConnectedRouter } from 'connected-react-router';
import Layout from './Layout';
class App extends React.Component {
static propTypes = {
store: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
};
render() {
const { store, profile, history } = this.props;
return (
<main className="app-wrapper">
// what i understand from change log is this part
// i need to pass context instead of store as props.
<Provider store={store}>
<ConnectedRouter history={history}>
<Layout user={profile} />
</ConnectedRouter>
</Provider>
</main>
);
}
}
function mapStateToProps(store) {
return {
...
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({
...
}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
as per change log i created context and passed it down to the provider
const storeContext = React.createContext(reduxStore);
here is my render function after that change
render() {
const { store, profile, history } = this.props;
return (
<main className="app-wrapper">
<Provider context={storeContext}>
<ConnectedRouter history={history}>
<Layout user={profile} />
</ConnectedRouter>
</Provider>
</main>
);
}
passing store as props to provider gives following error
Passing redux store in props has been removed and does not do anything. To use a custom Redux store for specific components, create a custom React context with React.createContext(), and pass the context object to React-Redux's Provider and specific components like: . You may also pass a {context : MyContext} option to connect
and passing as context gives following error
Could not find "store" in the context of "Connect(App)". Either wrap the root component in a , or pass a custom React context provider to and the corresponding React context consumer to Connect(App) in connect options.
i did not find any documentation expect this redux history document here it tells all the problems and solutions for the problem in react-redux and how the context api fixed it. but i am not sure how to actually implement it in real project.
did anyone face the same issue ? or can you please tell me how exactly to implement this change.
thanks
I was able to solve the problem by actually listening to what the error message said.
there were two problems with my code
i was passing store as props to my <App /> component. which is why the first warning/error message was comming.
Passing redux store in props has been removed and does not do anything. To use a custom Redux store for specific components, create a custom React context with React.createContext(), and pass the context object to React-Redux's Provider and specific components like: . You may also pass a {context : MyContext} option to connect
to fix this simply dont pass whole redux store as props to any component
my Provider from react-redux was not the root component. the error message said
Could not find "store" in the context of "Connect(App)". Either wrap
the root component in a Provider , or pass a custom React context provider to
and the corresponding React context consumer to Connect(App) in
connect options
so i followed the second wanring in the sentence
Either wrap the root component in a Provider , or pass a custom React context
so i wrapped my main root in provider. and things started working well.
ReactDOM.render(
<Provider store={reduxStore}>
<App />
</Provider>, document.getElementById('root'),
);
I had the same problem and this is how i solved it.
const MyContext = React.createContext();
class App extends Component {
render() {
return (
<Provider store = {store} context={MyContext}>
<BrowserRouter>
<div>
<Main context={MyContext}/>
</div>
</BrowserRouter>
</Provider>
);
}
}

wrap multiple react DOM component in redux's Provider

I have this code on my index.js file in my ReactJS project and I want the redux's <Provider> tag to wrap them up so that they can all access the same store, The question is, how can I do that?
ReactDOM.render(<Header />, document.getElementById('header'));
ReactDOM.render(<App />, document.getElementById('root'));
ReactDOM.render(<Footer />, document.getElementById('footer'));
Well, if the store is the same, then you can simply apply the <Provider> to all pieces and they will all use the same Redux store. The Provider is not mandatory to be unique, just the store. Something like this:
const store = createStore(...); // or any valid Redux store declaration or import
ReactDOM.render(<Provider store={store}><Header /></Provider>, document.getElementById('header'));
ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));
ReactDOM.render(<Provider store={store}><Footer /></Provider>, document.getElementById('footer'));
As another answer already suggests, this can be achieved by using same store in Redux provider. Since Redux isn't tied to React component hierarchy, connected components don't necessarily should have a common parent:
ReactDOM.render(
<Provider store={store}><Header /></Provider>,
document.getElementById('header')
);
ReactDOM.render(
<Provider store={store}><App /></Provider>,
document.getElementById('root')
);
ReactDOM.render(
<Provider store={store}><Footer /></Provider>,
document.getElementById('footer')
);
Another option that isn't specific to Redux but can also be used with any React application that has several root components is to use portals for these components, as shown in this answer:
const Page = props => (
<Provider store={store}>
{ReactDOM.createPortal(<Header/>, document.getElementById('header'))}
{ReactDOM.createPortal(<App/>, document.getElementById('root'))}
{ReactDOM.createPortal(<Footer/>, document.getElementById('footer'))}
</Provider>
);
ReactDOM.render(<Page/>, document.getElementById('page'));
Where <div id="page"></div> is placeholder element that exists in HTML body to mount the application and doesn't have to be a parent to header, etc. elements.
This option can be used with React context API Provider or page component state as well.
Is there a reason that you need to target three html elements? The standard practice here would be to just target root and handle the rest in your React application, like below:
In index.js:
const store = createStore(...);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>, document.getElementById('root'));
In App.js:
export default = () =>
<Header />
<Body />
<Footer />
Your entire HTML body would look like this (recommended approach by Facebook's create-react-app):
<body>
<noscript>JavaScript must be enabled to run this application.</noscript>
<div id="root"></div>
</body>

Passing data between External js and React js/Redux

I've integrated React-redux in a externalJs Application (built on custom JS framework). I need to set initial data for redux store from externalJS but, the externalJs is unable to access the react store to set the data. The store gets triggered when the root reactJS component is mounted on the DOM , but i need to set the initial data before its Mounted on the DOM. i referred following links but they were not able to resolve my problem. Can someone please tell me what I am missing?
https://brettdewoody.com/accessing-component-methods-and-state-from-outside-react/
Accessing react components outside
i'm using webpacks, react 16.1 with redux
sample root component structure given below in index.js
render() {
return (
<Provider store={store}>
<Email ref={(EmailComponent) => { window.EmailComponent = EmailComponent }} />
</Provider>
);
}
I'm assuming you're trying to expose EmailComponent and 'store' to the DOM and other frameworks since you are declaring it on a global window object. React.render has a callback as a third parameter which you can use to know when the React App is mounted to DOM.
ReactDOM.render(<App />, document.getElementById('root'), function () {
// now the root React App is mounted and the data from it will be available
// your window.EmailComponent and window.store should now be avialable
console.log(window.EmailComponent, window.store)
})
render() {
window.store = store
return (
<Provider store={store}>
<Email ref={(EmailComponent) => { window.EmailComponent = EmailComponent }} />
</Provider>
);
}

React error 'Failed propType: Invalid prop `children` supplied to `Provider`, expected a single ReactElement'

i have some problem with React.js.
this is my code:
import React from 'react';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import App from './containers/App';
import todoApp from './reducers';
let store = createStore(todoApp);
let rootElement = document.getElementById('root');
React.render(
<Provider store={store}>
{() => <App />}
</Provider>,
rootElement
);
and runs the page, it saids:
Failed propType: Invalid prop children supplied to Provider, expected a single ReactElement
this is the list of my related installed node modules:
react 15.0.1
react-redux 4.4.5
redux 3.4.0
actually, i am currently learning React with Redux, so it is hard to me how should i do. i just followed tutorial on website(i can give a link, but it is not english) but it is just not working with that error message.
as i searched, someone said upgrade version of react and react-redux, but i installed latest versions.
any advice will be very appreciate it.
According to the doc, you can just use a normal React element instead of a function inside <Provider />.
As a result, simply change your code to
<Provider store={store}>
<App />
</Provider>
I think this has changed since react#0.14.
The original question had a typo. Your response is correct in the proper syntax.
However, the direct reason is that <App /> needs to be on a separate line.
If you put it on the same line as <Provider store={store}> React/Redux tries to do more with it than it should.
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
I was brought to this and I simply forgot to provide a prop. I would say make sure that you are providing all the props that your component is using and that should fix it.

How to test a component with a nested container with React and Redux?

Due to the complexity of the application I am working on I have decided on using a nested redux container rather than passing an action as a prop down to the child components. However, this has proved to be problematic for unit testing when rendering the OuterContainer with jsdom in combination with mocha, chai and sinon.
Here is a contrived example of the view structure:
<OuterContainer>
<div>
<InnerContainer />
</div>
</OuterContainer>
where OuterContainer & InnerContainer are wrapped with connect. e.g.:
export connect(<mapStateToProps>)(<Component>)
When running tests the error I am getting is:
Invariant Violation: Could not find "store" in either the context or props of "Connect(Component)". Either wrap the root component in a `<Provider>`, or explicitly pass "store" as a prop to "Connect(Component)".
Is there a way to unwrap or stub the InnerContainer for unit testing without having to use shallow rendering?
Wrap your component in <Provider> when testing. It’s up to you whether to supply a real store or a mock with { dispatch, getState, subscribe } to it. Wrapping the outermost component in <Provider store={store}> will also make the store available to the child components at any level of nesting—just like in the app itself.
const store = createStore(reducer) // can also be a mock
ReactTestUtils.renderIntoDocument(
<Provider store={store}>
<OuterContainer />
</Provider>
)
Another approach is to export both the component to be connected and the container. The container as default, of course.
export const Comp = (props) => (<p>Whatever</p>)
export default connect(...)(Comp)
Hence, you can unit test Comp.
Not sure if this is what your problem is, but I'm sure this will probably help a few people out there looking at this feed.
I had the same error and it was a simple fix:
I had forgotten to pass my component my store object in my entry file (using webpack).
I just added an attribute to the Root component "store={store}" see below:
document.addEventListener("DOMContentLoaded", () => {
const store = configureStore();
ReactDOM.render(<Root store={store} />,
document.getElementById('content'));
});
This was my root file code for reference as well:
import React from 'react';
import { Provider } from 'react-redux';
import App from './app';
const Root = ({ store }) => (
<Provider store={ store }>
<App />
</Provider>
);
export default Root;
Hope that helps someone!
Mock the Provider component to return the child component.
Add this before describe().
jest.mock('Provider', () => ({children}) => children);

Categories