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

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

Related

Store with multiple reducers from different components. React / Redux

I have a store that needs to connect to different components. I created 3 different slices and wanted to make a dependency with the store.
When I hook all of three reducers :
export const store = configureStore({
reducer : {
home : homeSlice,
about : aboutSlice,
review : reviewSlice,
},
});
I get the next error :
Invalid hook call. Hooks can only be called inside of the body of a function component.
You are somehow using store incorrectly.
The Store needs to be passed to the Provider (which wraps all the components inside app.js).
import {store} from 'your_path'
const App = () => (
<Provider store={store}>
<MyApplication />
</Provider>
)
After that, you can already use Redux inside components.
To change states, use the useDispatch() hook,
and to get the state useSelector().
Here is a link to documentation about hooks in React-Redux https://react-redux.js.org/api/hooks
As in react, documentation hooks can be called only inside of a functional component. The code you provide to merge reducers is correct. You need to use useSelector and useDispatch hooks inside the component functions to retrieve the value or dispatch an action respectively. What is the main issue you are facing?

Fetch data for the entire app without depending on component logic

There are X routes in the frontend made with React-router and each of them drives to a component.
No matter which route is visited, I need to use some data from an api.
This data is constant through the application. Using Redux I could dispatch an action FETCH_DATA in each React component and manage the logic of fetching in Redux actions.
However, this looks repetitive to me because I would be writing the same logic through all the React components that need data: look for the the data in the store. If it's there take it. If it's not there, dispatch FETCH_DATA.
What is another approach?
You can call that api in the App.tsx i.e. your first component to be rendered in the application. This is the component which actually holds the Routes also.
Consider the following code as an example and change it according to the need of your application:
class App extends React.Component {
ComponentDidMount() {
this.props.callTheApiHere()
}
render() {
return (
<Routes history={this.props.history} />
)
}
}
export default App;
Now each component that any of your Route render have to just get the data from the redux store using mapStateToProps in react-redux Connect
GraphQL offers an elegant solution for this requirement of sharing data across components belonging to different routes.
More information:
https://www.youtube.com/watch?v=p_kqcGW1jkY
Apollo GraphQL (React) Data sharing across components

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

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

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.

Mocking Redux store when testing React components?

I'm using React and Redux. I have a component which loads ChildComponent and depending on Redux's state will also load MainComponent
const ChooseIndex = ({ appInitMount }) => {
return (
<>
<ChildComponent />
{!appInitMount && <MainComponent />}
</>
);
};
const mapStateToProps = ({ main }) => {
return {
appInitMount: main.appInitMount
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(ChooseIndex);
I'm trying to write a test to check that ChildComponent is loaded:
import React from "react";
import { render } from "react-testing-library";
import ChooseIndex from "../choose-index";
test("ChooseIndex should call ChildComponent", () => {
const wrapper = render(
<ChooseIndex />
);
});
I get this error:
Error: Uncaught [Invariant Violation: Could not find "store" in either
the context or props of "Connect(ChooseIndex)". Either wrap the root
component in a , or explicitly pass "store" as a prop to
"Connect(ChooseIndex)".]
Should I mock Redux by passing an object literal to ChooseIndex? Or should I create a Redux store (as my real application does) for every test?
Try to render your component like this:
render(
<Provider store={store}>
<ChooseIndex />
</Provider>
)
And pass the actual store you use in your app. In this way, you're testing the real logic that you'll use in production. You also don't care what actions get dispatched and what's in the state. You look at what gets rendered and interact with the UI—which is what matters in the end.
Separating the component from Redux and testing the two in isolation is against the whole point of react-testing-library. You want to test your app as a real user would.
If you check out the writing tests section of the redux docs, there is an example of testing a connected component.
when you import it [A redux connected component], you're actually holding the wrapper component returned by connect(), and not the App component itself. If you want to test its interaction with Redux, this is good news: you can wrap it in a with a store created specifically for this unit test. But sometimes you want to test just the rendering of the component, without a Redux store.
In order to be able to test the App component itself without having to deal with the decorator, we recommend you to also export the undecorated component
As with most unit tests, you really want to be testing your components, and not that redux is working correctly. So the solution for you is to export both the component and the connected component, while only testing the component itself, and providing whatever props redux is passing to your component.
import { connect } from 'react-redux'
// Use named export for unconnected component (for tests)
export class App extends Component {
/* ... */
}
// Use default export for the connected component (for app)
export default connect(mapStateToProps)(App)

Resources