React with Redux? What about the 'context' issue? - javascript

I normally post code related stuff on Stack, but this is more a question about what the general thoughts of the community are.
There seems to be a lot of people advocating the use Redux with React to manage data/state, but while reading and learning both I've come across something that doesn't quite look right.
Redux
At the bottom of this page: http://redux.js.org/docs/basics/UsageWithReact.html (Passing the Store) it recommends using the "Magic" of React 'Context'.
One option would be to pass it as a prop to every container component. However it gets tedious, as you have to wire store even through presentational components just because they happen to render a container deep in the component tree.
The option we recommend is to use a special React Redux component called to magically make the store available to all container components...
React
On the React Context page (https://facebook.github.io/react/docs/context.html) it has a warning at the top:
Context is an advanced and experimental feature. The API is likely to change in future releases.
Then at the bottom:
Just as global variables are best avoided when writing clear code, you should avoid using context in most cases...
Do not use context to pass your model data through components. Threading your data through the tree explicitly is much easier to understand...
So...
Redux recommends using the React 'Context' feature rather than passing the store along down to each component via 'props'. While React recommends the opposite.
Also, it seems that Dan Abramov (the creator of Redux) now works for Facebook (the creator of React), just to confuse me more.
Am I reading all this right..?
What is the general current consensus on this issue..?

Context is an advanced feature and is subject to change. In some cases its conveniences outweigh its downsides so some libraries like React Redux and React Router choose to rely on it despite the experimental nature.
The important part here is the word libraries. If context changes its behavior, we as library authors will need to adjust. However, as long as the library doesn’t ask you to directly use the context API, you as the user shouldn’t have to worry about changes to it.
React Redux uses context internally but it doesn’t expose this fact in the public API. So you should feel much safer using context via React Redux than directly because if it changes, the burden of updating the code will be on React Redux and not you.
Ultimately React Redux still supports always passing store as a prop so if you want to completely avoid context, you have that choice. However I would say this is impractical.
TLDR: Avoid using context directly unless you really know what you are doing. Using a library that happens to rely on context internally is relatively safe.

I don't know about others, but I prefer using react-redux's connect decorator to wrap my components so that only the props from the store I need are passed into my component. This justifies the use of context in a sense because I am not consuming it (and I know, as a rule, any code that I am in charge of will not consume it).
When I test my components, I test the non-wrapped component. Because react-redux only passed the props I needed on that component, I now know exactly what props I need when I'm writing the tests.
I suppose the point is, I don't ever see the word context in my code, I don't consume it, so to a certain degree, it doesn't affect me! This doesn't say anything about Facebook's "experimental" warning.. If context disappeared, I'd be just as screwed as everyone else until Redux was updated.

There's an npm module that makes it really easy to add redux to the react context
https://github.com/jamrizzi/redux-context-provider
https://www.npmjs.com/package/redux-context-provider
import React, { Component } from 'react';
import ReduxContextProvider from 'redux-context-provider';
import createStore from './createStore';
import actions from './actions';
import Routes from './routes';
export default class App extends Component {
render() {
return (
<ReduxContextProvider store={store} actions={actions}>
<Routes />
</ReduxContextProvider>
);
}
}

React ships with all the features you need to handle your state without a single additional library. Most of your application's states should not be global as they live just fine in a useState or useReducer or custom hook next to your components.
So before you dive into the world of advanced state management (e.g. Redux), consider using the tools React ships with out of the box.
If you are interested in learning a bit more about this, I'd recommend this article by Andy Fernandez, which dives into the details on Redux: Context API vs Redux

Related

I wonder if I can have a redux store in the library and the App also have a redux store

I'm really new to React and need to ask.
Can I have a ReactJs Redux store in a library and then use that library in an App that also has a Redux store?
Both of them do this:
<Provider store={store}>
..App
</Provider>
I learn ReactJs and am not sure I understand how this is built up how Webpack is loading the code here.
Will these two React Stores collide?
Can they exist independently?
Can they share Reducers? (let's say the App want to use the library Redux store and send a dispatch to it )
I have tested doing some of this but can't make it work. It's like Redux after first loading the library Redux store then it can't load the App store but I'm a novice so must ask
Will these two React Stores collide? / Can they exist independently?
Two different stores created with createStore will not collide, and can exist independently.
behind the scenes, each store instance has a subscribe method, and its own subscribers array.
When using the react-redux Provider component, you are sending an instance of the store down the component tree with React's context API. The instance will be available to all children and decedents of the component which rendered the Provider. If there is another Provider in the way, that Provider's value will override the higher up Provider.
Thus, if you use another Provider with another instance of a store in your library, it will take effect only for the component tree starting from your library component. With the correct composition, there will be no collision.
Can they share Reducers?
Reducers are nothing but pure functions, meaning they shouldn't have any side effects. So you could export and reuse the same reducer logic if you want, you'll just need to register them with every store instance.
Lastly, I disagree with other answers here which claim you shouldn't use multiple stores. You have the exact use case where a separate store would be justified, where you have your main application using one store, and you have a standalone library that uses another unrelated global state.
In my opinion, everything is possible in the programming world, definitely, having a multi-store is possible too.
You asked several questions, first of all, I answer them and then explain a little bit more.
Can I have a Reactjs Redux store in a library and then use that library in an App that also has a Redux store?
Yeah, it's possible, the famous library that makes Redux easy to use is Redux Toolkit, which has a very easy flow to use and implement in your application, even it has a CRA template that you can create your application (zero-config) based on redux npx create-react-app [my-app-name] --template redux or redux-typescript npx create-react-app my-app --template redux-typescript. it works properly.
Will these two React Stores collide?
Can they exist independently?
No, they won't collide because each store (never mind it is redux, mobx, or whatever) has a Provider and you should wrap part of your application or entire of it by using that <Provider store={store}>, so if you wanna have two stores, you can add two Providers and they won't collide. but, in connecting, and getting some data from stores, you should pay attention that which Provider you are going to call from. so they will be able to exist independently.
<ReduxOneProvider store={storeOne}>
<ReduxTwoProvider store={storeTwo}>
<MobxProvider store={mobXStore}>
<App>
</MobxProvider>
</ReduxTWoProvider>
</ReduxOneProvider>
But, totally, I'm not a fan of having multi-store, for more info read here
Can they share Reducers? (let's say the App want to use the library Redux store and send a dispatch to it )
Yes, you know, reducer functions are separate pure functions, located in a folder, when you wanna build your stores, you should gather these functions and combine them, so, the answer is yes, but please consider, the connect function which comes from react-redux want two functions, mapStateToProps and mapDispatchToProps, inside the second you can call a reducer by using dispatch function. so you will have re-render in all stores.
my opinion:
Please avoid having a multi-store, even having one and dealing with it, makes the project a little bit hard to maintain. how you wanna deal with multi. it makes complicated issues.
Yes it is possible.
To keep it simple, library is completely independent package where you can use the redux in normal way. And as you export the library's components to outer world, in same way export the store or dispatch which you would like to use in your application which is consuming the library.

When should I use Vuex?

Now I start to learn vue, and I'm creating SPA for editing database. Now I can't understand where I should use a Vuex. I can use props and $emit everywhere and it can help me find needed parameter. So for what case I should use Vuex?
According to this awesome tip from Vuedose blog
Vue.js 2.6 introduced some new features, and one I really like is the new global observable API.
Now you can create reactive objects outside the Vue.js components scope. And, when you use them in the components, it will trigger render updates appropriately.
In that way, you can create very simple stores without the need of Vuex, perfect for simple scenarios like those cases where you need to share some external state across components.
For this tip example, you’re going to build a simple count functionality where you externalise the state to our own store.
First create store.js:
import Vue from "vue";
export const store = Vue.observable({
count: 0
});
If you feel comfortable with the idea of mutations and actions, you can use that pattern just by creating plain functions to update the data:
import Vue from "vue";
export const store = Vue.observable({
count: 0
});
export const mutations = {
setCount(count) {
store.count = count;
}
};
Now you just need to use it in a component. To access the state, just like in Vuex, we’ll use computed properties, and methods for the mutations:
<template>
<div>
<p>Count: {{ count }}</p>
<button #click="setCount(count + 1);">+ 1</button>
<button #click="setCount(count - 1);">- 1</button>
</div>
</template>
<script>
import { store, mutations } from "./store";
export default {
computed: {
count() {
return store.count;
}
},
methods: {
setCount: mutations.setCount
}
};
</script>
Update: Since this answer was written, things changed. If you're considering using Vuex today, don't. Use Pinia instead. Here's a decent answer outlining why you should favor Pinia over Vuex. However, what I said about the Redux pattern below, still stands.
The main purpose of using Vuex (which is Vue's version of Redux) is state (or data) management. In layman's terms, it's having a single source of truth (for your data).
It opposes the common pattern of emitting an event when data changed so other parts of the application can keep their data in sync with the current component's data.
Rather than allowing one particular component to change the data directly, you delegate the change to a separate module: the store. Any component using that data will be notified about the change. This becomes extremely useful in complex applications, where the common emit pattern runs the risk of creating cyclic update loops.
Apps using the Redux pattern benefit from:
predictability of outcome,
ease of testing
code maintainability and scalability
separation of concerns
ease of debugging (time travel: the ability to undo or replay a particular change for debugging purposes).
In short, it gives developers control and confidence, as it makes understanding and modifying complex code, data or app behavior significantly easier.
Like any other pattern (or system, or tool), the Redux pattern can be over-used. It should be obvious you don't have to keep data that's only used by one component in the store. Since no other component needs to know about any changes to that data, one should manage it in the component using it.
Vuex allows organisation of data into modules so you can keep everything tidy and maintainable.
Last, but not least, one of the most useful benefits of using the Redux pattern is making SSR (server side rendering) implementation a breeze. SSR greatly improves optimization and user experience and increases the perceived performance of the application.
Yes, you can do anything without the use of Vuex, but with time, if your application is getting larger then it would be difficult to maintain,
according to vuex documentation,
problem one, passing props can be tedious for deeply nested
components, and simply doesn't work for sibling components. problem
two, we often find ourselves resorting to solutions such as reaching
for direct parent/child instance references or trying to mutate and
synchronize multiple copies of the state via events. Both of these
patterns are brittle and quickly lead to unmaintainable code.
Hope it answers your question.
Look at the vuex documentation; it describes all the reasons why/when you want to use vuex https://vuex.vuejs.org.
For instance, multiple components require the same information, controlling mutations, validations, etc.

React architectural problem - Best way of having a single global application state, updating child components when changed

I'm quite new to React, and I'm making a single page application with React.
So far, I've build the application with components and child components, having their own local state, however the child components doesn't really interact with one another, which is what I want them to, basically, with the least amount of boiler plate code...
The problem I'm facing, is that a change in some child component, should be able to update the state of another child component, somewhere else in the component tree.
A selection in one child component should also be able to trigger a function in another component updating it with data and so on.
I've considered having just one global application state, that all components can call and update when something in them changes, and this one application state will then update other components in the tree. Kinda like having a single "controller" with it's own state, that all components "views" can call, and which updates the states of other components as needed. (I'm used to WPF and MVC style of GUI programming).
What I've considered:
One could try to implement this with callback functions defined in the top of the hierarchy, to be sent down through the hierarchy and called from a child component when it changes.
This method however results in a LOT of boilerplate code that just passes functions to their child components. It feels wrong and hard to maintain...
To avoid all this passing around and boilerplate code, I've tried using a React Context, however this is not working as well as I hoped. I can only access the context from within the render function and from lifecycle functions, and sadly I often get complicated errors that are hard to understand. It seems like I'm exploiting React Context to do something you shouldn't use it for...
I've considered using a singleton pattern in JavaScript, however then that singleton needs to have a reference to the root component, and query for the component it needs to change... This seems like kind of a hack, and may not be that pretty, but idk.
I'm considering trying out React Redux however it seems to work in many ways similar to React Context (I'll be honest, I haven't read much into it yet).
What I need:
I need to ask someone with greater React experience than me: How do you keep a global application state, and update child components based on changes to the global application state? Also: Am I thinking about this all wrong? Am I trying to do something in a non-react way, failing to see how I should do it in React?
You can happily go with Redux or MobX, they're fine.
I suggest Taming The State from Robin Wieruch: https://roadtoreact.com/course-details?courseId=TAMING_THE_STATE
There are the book and the course. He shows different ways of handling React state.
Redux was created specifically for the problem yo stated.
Reacts follows a top-down down-top unidirectional flow in essence. Context API is useful in simple use cases but would fail horribly in a large scale application where you'd be creating consumers everywhere.
I'd suggest investing some time in Redux so that will save your precious time in long run.
There's a reason all big three frameworks require a state management library to be useful for large scale complex apps. (Angular has NgRx and Vue has Vuex).

React Component State management using redux

So Basically, I came across many articles where they are referring to manage state via flux or redux.
I wanted to know that how about UI component having its own state? Is it good practice to let redux manage the Api call and success toast messages etc. but UI components should have their own local state?
Kindly someone elaborate what is the best practice in industry?
Though the question calls for opinion, I am going to leave my answer. There is no standard best practice regarding this. It boils down to convenience and ground rules of your team, if there are more than one people writing the code.
I use Redux quite a lot. However, I won't refrain from using local state in components.
Form handling like input onChange handlers require local state. It is not performant to use global state for onChange handlers.
Reusable component uses local state. Again, it boils down to whether the reusability is a technical reusability or business reusability. If you are developing a custom scrollbar component, use local state. However, if you are using a comment form which is used everywhere in your application, use global state.
I prefer to have most of the stuff in global state. I use redux thunk as well. In redux thunk, it is possible to access global state within the thunk function. This is quite useful as it avoids the reliance for props / context being passed all around.
I do keep some simple things in local state -- for example, show / hide some stuff. I don't mind waiting for promises to resolve before hiding some stuff using local state.
Overall, the decision to use global state vs local state is primarily based on convenience. There are no standard rules other than what you and your team are comfortable with.
React is a way to declaratively deal with UI. There are some rules of the framework like state, props, context. It is left upto the developer to make the UI declarative and performant based on these primitives. How, a developer does it does not matter as long as the code is maintainable and understood by others.
After asking many professionals and industry developers, I came to know that managing state through redux depends on your application scope.
but more importantly, If I am working on enterprise Application then the application state must be managed through redux.
Now the question is what should be kept in our redux store. well, you can store almost anything in redux store but better to manage the local state of a component as well. for instance, opening a component Boolean should be managed in local state or any string or header name etc.
Good question! The answer is usually "it depends", but there are clear cases when you'd probably prefer to use one over the other.
Use Redux for storing state relevant to the application. E.g. the current page/panel that should be shown. As you mentioned, showing a notification/message - is something that'd make sense to store in redux state, as the alternative would be passing state all over the place, e.g. an error prop bubbling up to your root component which renders the toast message. Using thunk is also a good idea when you're fetching/manipulating data relevant to the whole app, e.g. a list of things that appear in several places.
Use component state for storing state only relevant to the component. That is, if you're filling in a form, it makes sense to store the value of text inputs, checkboxes etc. in your component state (perhaps in conjunction with container and presentational components) as the values at this point aren't relevant to the rest of the application.

ReactJS - Global state outside of components

I've been reading and watching videos on Redux and seeing a lot of debate about managing state in components or with Redux. What I'm not seeing is anything about managing state completely outside of components with standard global variables.
For example, I could set a global state variable like this:
let state = {
player: {
username: "BillyBob",
score: 100
}
}
Then in a React component, I could have something like this:
incrementScore() {
state.player.score += 1
props.update()
}
Then in App.js, I could have this:
update() {
this.forceUpdate()
}
I realize that I'd still have to pass the update function down through the tree, but I'd at least be able to set functions at the component level and not have to worry about passing multiple pieces of state and functions to child components.
I'm new to React, but the only downside I can think of is not being able to require propTypes. Is there anything else I'm missing?
EDIT: Per request that I clarify the question, are there any major downsides to the implementation above that I should be considering that would affect even a relatively simple app?
If you look at the implementation of redux or some other state management library out there (for example mobx or mobx-state-tree), basically all of them maintain the state outside of the component as a standalone object.
However, to detect changes and trigger re-render efficiently, they implement a HOC, it's connect in redux and inject in mobx, what the HOC (higher order component) does is to wrap your component inside another component that have access to the global state, and pass the part of the state require by your component via its props. This way, the component only re-render when the data required by it changes.
Compared with these popular library approach, there are couple problems with your proposed solution.
The first is using forceUpdate, basically, the last thing you might want to do is to call forceUpdate on the root node of your app, think about scenario when someone type in an input and the whole app re-render every single keystroke.
The second will be passing an update function down to multiple level of children, it will be fine if you have only 1 or 2 nested component, but will be a big problem one your app grows. As your app grows and your state become more complex, it might not be the best idea to have a single update function to control the whole state object.
React exists to solve the problem of creating a user interface that is composed of several indepentent pieces that can be developed in parallel and can seamlessly interact with each other.
If you are going to use global namespace to define your state then you are going to bypass most of the key features of React such as:
1. Lifecycle methods
2. Virtual DOM
3. Controlled Components
4. Rendering Optimizations
In a nutshell, you are going to end up with all the overhead costs of running React while missing out on its benefits.
The 'catch' in learning a new framework or paradigm is to understand how to define the problem in such a way that it leads to path of least resistance. This can be achieved by introducing a constraint and then solving the problem within that constraint.
By favoring vanilla JavaScript for state management, you are not giving React and Redux a fair chance.
I've created a library exactly for this use case :)
Simple 💪 fast ⚡️ and small 🎈 (500 bytes) global state management for React which can be used outside of a React component too!
https://github.com/web-ridge/react-ridge-state
Stick to redux, dont complicate things for yourself :)

Categories

Resources