React-Redux: Why do we need to wrap the component with Provider? - reactjs

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.

Related

Difference between using React-Redux <Provider> and connect()

I am confused about why a create-react-app has both
import {store} from './our-redux-store'
const app = () => {
return (
<Provider store={store}>
<WholeAppGoesHere/>
</Provider>
)
}
versus using connect with a component like so:
import {connect} from 'react-redux';
class MyComp {
// ...
}
export default connect(
mapState,
mapDispatch
)(MyComponent);
do we need both? what's the difference?
The provider is a component. You use the provider at the top of the component chain where your application starts. You place it so it wraps your entire application. Within this container, you pass the store. This allows any child component to access the store.
connect() is used as a higher-order function that you wrap around specific components. Connect in essence maps state data contained within the store to the props within that specific component. Maybe it helps to think of connect() as a way an individual component gets the specific data it needs from the global store
Provider is a part of React Context functionality. By assigning your store there, it makes the specific value of store available to all consumers of that context.
connect() on the otherhand is a higher order component that injects your mapped states and dispatches into the props of your base component. To do so, it calls the Consumer part of this api to access the store context.
Edit: https://react-redux.js.org/using-react-redux/accessing-store#understanding-context-usage

Using React-Redux: How to Access Variables in the Store, within Nested Components

I am using React-Redux, but I am not able to figure out how to access a variable in the Redux store inside of my nested components.
How can I share a variable between components, using React-Redux?
For example:
I have an 'index.js' file and 30 nested components. Managing these components becomes difficult after a while.
I have a 'C1.js' component. Let's just say I wrote this code in it.
function Reducer(state = 'example' , action) {
return state;
}
const store = createStore(Reducer)
index.js file:
<Provider store = {store}>
<App/>, document.getElementById('root')
</Provider>
How do I pass the 'store' variable to the 'C1.js' component to the index.js file?
Thanks...
You need to use something called "Connect" to connect your various components to the provider.
In the file that contains your C1.js component:
import {connect} from 'react-redux'
const MyComponent = () => {
let someData = props.someData
return(
//all of your JSX for your component here
)
}
const mapState = state => {
return {
someData: state.someData
}
}
export default connect(mapState)(MyComponent)
In the code above, notice the mapStateFunction. Connect is hooking that up with the Provider, and the state that is on the Provider. So that is where you are able to link whatever properties are on your Provider (React-Redux) state with this particular data.
Now, in your component, you will now have prop.someData
-
In the index file, you have your Provider in the wrong place, you need to change your code to this:
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app')
)
See, the difference there? The is the React Element (and all of its children that you are asking React to render to the DOM). It is the first parameter of the ReactDOM.render function.
The second parameter to the ReactDom.render function is the element in the DOM where you want it to put all of your React elements.
You did not configure well redux and react. You need to go over the doc of redux to setup correctly. Should get working after that.

How do I write a storybook story for a component that has redux-connected component as grandchild?

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')

What's the point of inject decorator if Provider is said to be already passing all the props down to all the children?

Consider a case:
import { Provider } from 'mobx-react';
import usersStore from './stores/usersStore';
import itemsStore from './stores/itemsStore';
const stores = { usersStore, itemsStore };
ReactDOM.render(
<Provider {...stores}>
<SomeComponent />
</Provider>,
document.getElementById('app')
);
So, doesn't SomeComponent in the sample above already get both usersStore and itemsStore as its props from Provider? Why is the #inject('itemsStore') line in the following sample even required?
#inject('itemsStore') #observer
class SomeComponent extends React.Component {
render() {
return (
<div className="index">
{this.props.itemsStore.items.map((item, index) => {
return <span key={index}>item.name</span>
})}
</div>
);
}
}
Provider is a component that can pass stores (or other stuff) using
React's context mechanism to child components. This is useful if you
have things that you don't want to pass through multiple layers of
components explicitly.
inject can be used to pick up those stores. It is a higher order
component that takes a list of strings and makes those stores
available to the wrapped component.
Provider and inject are abstractions to React context API (which until recently was quite unstable).
The Provider makes data available from components context, while inject HOC provides a simple API to declare what we want out of the context and passes it to the wrapper component.
The same works other libraries like react-redux.

How to access store in second component in react-redux

I have a single component App.js where I trying to save state using redux. In index.js where I set store for only <App /> component.
index.js
let store = createStore(scoreReducer);
ReactDOM.render(
<Provider store={store}><App /></Provider>,
document.getElementById("root")
);
registerServiceWorker();
I have this method in App.js to map state to props which is available inside App.js.
App.js
function mapStateToProps(state) {
return { score: state.score, status: state.status };
}
Everything is well so far, now I am not sure how to access { this.props.score} in another component ?
What changes I need to do in index.js and second component if I want to access {this.props.score} in another component ?
When you are using Provider any component that is children of the Provider Higher Order Component can access the store properties though the use of connect function.
So you can add the following in any component that is a child of Provider and access the score prop
function mapStateToProps(state) {
return { score: state.score, status: state.status };
}
export default connect(mapStateToProps)(MyComponent)
However if this other component is a direct child of App component then you can also pass the score prop as a prop to this component from App like
<MyComponent score={this.props.score}/>
Provider component sets the context for all its children, providing the store in it. when you use the High Order Component(HOC) connect you can wrap any component and access the store through the provided mapStateToProps and mapStateToProps no matter how nested they are. You can also access the store using context context.store but this is not recommended. Using map functions and connect, similar to what you have with your App component, is the best approach.

Resources