React Redux: How to share data between reducers? - javascript

I have a basic todo list. The todo input field for the todo list has an onChange event that triggers an action and sends event.target.value to a reducer and stores each character the users types to a property of the store object.
When the user submits the form I want to get the data that was previously stored via the onChange event, then I want to place it on a new property on the store object.
How do I get data that was previously entered from the store and pull it into a different reducer?
In all the examples I've seen, reducers start with an "initial state". I don't want that, I want the previous state that the user entered.
Below is a CodeSandbox version of the code (for some reason the orange-beige tab to the right needs to be switch to the left to blue for it to render the form. If you don't do that it won't work).
https://codesandbox.io/s/pwqlmp357j

Ask yourself whether you've structured your reducers correctly. If a
and b are not independent of one another, why are they separate
reducers?
Well if we talk about correctly structuring the reducer, cachedInput and todoList should live in single reducer. There is no need to create another reducer. Also I guess you need to grab the input value all at once(when user clicks on submit) and send it to store.
If still you want to create seperate reducers then you can follow the approach given below :
Answer to How to access or share data already in store, inside the reducers?
Don't use combineReducers.
Example
replace this code
export const a = combineReducers({
cachInput,
newTodo
});
with
export default (state = {}, action) => {
return {
cachInput: cachInput(state.app, action, state),
newTodo: newTodo(state.posts, action, state),
};
};
reducer would be like
const reducer = (state = initialState, action, root) => {....}
and you can access the previous state from root

onChange event that triggers an action and sends event.target.value to a reducer and stores each character the users types to a property of the store object
Practically, this is not right way to use to redux->action->reducers->store considering you are submitting the form to send the same data to reducers.
This will caused un-necessary rendering of component connected with redux#connect if you have not handled shouldComponentUpdate /componetWillRecieve/getDerivedStateFromProps nicely
Instead store the each character/string that you typed, inside component state and once the user submit it, dispatch the action to pass the string/characters to reducer's.

You are using two different state(or say initialState) for each reducer. Hence update in one object is not reflecting in the other.
You need to use the shared initialState for both of the reducer and your problem is solved.
You can keep initialState in different file and you can import in both the reducers.
Consider below code sample:
1.InitialState.js
export default initialState = {
todoList: [
{ text: "fake todo" },
{ text: "fake todo" },
{ text: "fake todo" }
],
cachedInput: ""
};
2.Now in CachDataOfTodoInput.js
import initialState from "InitialState";
export function cachInput(state = initialState, action)
3.Same in SubmitDataToTodo.js
import initialState from "InitialState";
export function submitNewTodo(state = initialState, action)
Hence intialState is shared between your reducers and you will be able to access data in each reducer.

If you want to load your application with initial state that contains previously cached data and if you are using client side rendering, you may insert cached data in redux store into local storage and use that as initial state.
If you just want data to be imported to the other reducer after form submission, you may use redux-thunk. It lets you load current state in your submit action and pass the catches values into the other reducer and store them.
if you are using ssr and you want data be loaded on server there is no way unless saving data on server ( in a file or database), read the data when request is received on server and update your redux store. It will load data all over your app.

Related

Callbacks using redux-thunk / redux-observable with redux

I am learning how redux works but its a lot of code to do simple things. For example, I want to load some data from the server before displaying. For editing reasons, I can't simply just use incoming props but I have to copy props data into the local state.
As far as I've learned, I have to send a Fetch_request action. If successful, a fetch_success action will update the store with new item. Then updated item will cause my component's render function to update.
In component
componentWillMount() {
this.props.FETCH_REQUEST(this.props.match.params.id);
}
...
In actions
export function FETCH_REQUEST(id) {
api.get(...)
.then(d => FETCH_SUCCESS(d))
.catch(e => FETCH_FAILURE(e));
}
...
In reducer
export function FETCH_REDUCER(state = {}, action ={}) {
switch (action.type) {
case 'FETCH_SUCCESS':
return { ...state, [action.payload.id]: ...action.payload }
...
}
Back in component
this.props.FETCH_REDUCER
// extra code for state, getting desired item from...
Instead, can I call a react-thunk function and pass some callback functions? The react-thunk can update the store and callbacks can change the component's local state.
In component
componentWillMount() {
this.props.FETCH_REQUEST(this.props.match.params.id, this.cbSuccess, this.cbFailure);
}
cbSuccess(data) {
// do something
}
cbFailure(error) {
// do something
}
...
In action
export function FETCH_REQUEST(id, cbSuccess, cbFailure) {
api.get(...)
.then(d => {
cbSuccess(d);
FETCH_SUCCESS(d);
}).catch(e => {
cbFailure(d);
FETCH_FAILURE(e);
});
}
...
Is this improper? Can I do the same thing with redux-observable?
UPDATE 1
I moved nearly everything to the redux store, even for edits (ie replaced this.setState with this.props.setState). It eases state management. However, every time any input's onChange fires, a new state is popping up. Can someone confirm whether this is okay? I'm worried about the app's memory management due to redux saving a ref to each state.
First of all, you should call your API in componentDidMount instead of componentWillMount. More on this at : what is right way to do API call in react js?
When you use a redux store, your components subscribe to state changes using the mapStateToProps function and they change state using the actions added a props through the mapDispatchToProps function (assuming you are using these functions in your connect call).
So you already are subscribing to state changes using your props. Using a callback would be similar to having the callback tell you of a change which your component already knows about because of a change in its props. And the change in props would trigger a re-render of the component to show the new state.
UPDATE:
The case you refer to, of an input field firing an onChange event at the change of every character, can cause a lot of updates to the store. As mentioned in my comments, you can use an api like _.debounce to throttle the updates to the store to reduce the number of state changes in such cases. More on handling this at Perform debounce in React.js.
The issue of memory management is a real issue in real world applications when using Redux. The way to reduce the effect of repeated updates to the state is to
Normalize the shape of state : http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html
Create memoized selectors using Reselect (https://github.com/reactjs/reselect)
Follow the advice provided in the articles regarding performance in Redux github pages (https://github.com/reactjs/redux/blob/master/docs/faq/Performance.md)
Also remember that although the whole state should be copied to prevent mutating, only the slice of state that changes needs to be updated. For example, if your state holds 10 objects and only one of them changes, you need to update the reference of the new object in the state, but the remaining 9 unchanged objects still point to the old references and the total number of objects in your memory is 11 and not 20 (excluding the encompassing state object.)

Why is ugly hack required for react-redux to re-render redux-form?

I have a react app that is using a redux store and this component also uses redux-form. I am seeing a strange behavior on one particular type of update where I want to trigger a follow-on update.
Basically a user is updating permissions for something on the form, then saves. The save submits the form and in the processing of the action, it triggers another action (getting a list of all the somethings). When the list is returned from the server, this dispatches an action to update the list UI. At this point I am updating a property in the redux store for the form to say trigger an update of the permissions for the currently editing thing. This is the only point where I can do this because we now have the information to trigger this request.
The problem is that a boolean flag is being set as the trigger, and the mapStateToProps is getting called with the updated flag, but the component is not subsequently getting called (shouldComponentUpdate and componentWillUpdate never get called). It is like it ignores the state of the boolean variable in the props.
I found that if, in mapStateToProps I set another property based on the
boolean trigger, then the component is updated (re-rendered).
function mapStateToProps(store, ownProps) {
...
let triggerFetchSomethingOwnershipHash = 0;
if (store.triggerFetchSomethingOwnership) {
triggerFetchSomethingOwnershipHash = 100000 * Math.random();
}
return {
...
triggerFetchSomethingOwnership,
triggerFetchSomethingOwnershipHash
}
}
...
const ConnectedSomethingDetail = connect(mapStateToProps)(SomethingForm);
export default ConnectedSomethingDetail;
Why do I need to do this and how can I avoid it?
Yes I have checked a few relevant SO questions like react-redux-component-does-not-rerender-on-store-state-change and react-redux-update-item-in-array-doesnt-re-render

Bind react component to part of redux state

I have redux store that looks something like this:
{
user: {},
alerts: [],
reports: [],
sourses: []
}
For each one of this parts of state i have a bunch of React Components wrapped in a container wich connected via react-redux. And has mapStateToProps like this
(state) => {alerts: state.alerts}
(state, ownProps) => {alert: _.filter(state, {id: ownProps.curId})}
Problem that when i for example launch some action for Alerts like CREATE_ALERT or EDIT_ALERT and redux state updated, ALL REACT COMPONENTS WILL RESPOND TO THIS CHANGE even ones that works with different parts like sources or reports.
My question: how to "bind" certain components to certain parts of a tree. So each container component WILL UPDATE ONLY WHEN APROPRIATE PART OF REDUX STATE UPDATED and ignore other changes.
Expected behavior
Dispatch CREATE_ALERT -> Alert reducer -> Redux store update -> ONLY Alert container component re-rendering.
When you are changing state in redux the whole state becomes just a new object.
Then your component is given by this new object (new reference) and re-renderes itself.
To fix this behaviour you need to add some logic to compare if your component got props with different value (not reference).
The easiest and fastest way is to use React.PureComponent. You can also override shouldComponentUpdate function and handle changes by yourself. But note that PureComponent works only with primitives (it does a shallow compare).
Check also Immutable.js which helps you with intelligent way of changing references of props.
if you use connect method, then pass only selected redux state to the component, this will prevent rendering of other components
example:
User Component:
const mapStateToProps = state =>({
users: state.users
});
export default connect(mapStateToProps)(User)
Alert Component:
const mapStateToProps = state =>({
alerts: state.alerts
});
export default connect(mapStateToProps)(Alert)
Check this out: Avoid Reconciliation
There explains what Neciu says.
Container components created with connect will always receive notifications of all updates to the store.
The responsibility for consuming these updates falls on the receiving connect component. It should contain the logic to extract the data relevant to it.

How to show a loading indicator in React Redux app while fetching the data? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
I'm new to React/Redux. I use a fetch api middleware in Redux app to process the APIs. It's (redux-api-middleware). I think it's the good way to process async api actions. But I find some cases which can't be resolve by myself.
As the homepage (Lifecycle) say, a fetch API lifecycle begins with dispatching a CALL_API action ends with dispatching a FSA action.
So my first case is showing/hiding a preloader when fetching APIs. The middleware will dispatch a FSA action at the beginning and dispatch a FSA action at the end. Both the actions are received by reducers which should be only doing some normal data processing. No UI operations, no more operations. Maybe I should save the processing status in state then render them when store updating.
But how to do this? A react component flow over the whole page? what happen with store updating from other actions? I mean they are more like events than state!
Even a worse case, what should I do when I have to use the native confirm dialog or alert dialog in redux/react apps? Where should they be put, actions or reducers?
Best wishes! Wish for replying.
I mean they are more like events than state!
I would not say so. I think loading indicators are a great case of UI that is easily described as a function of state: in this case, of a boolean variable. While this answer is correct, I would like to provide some code to go along with it.
In the async example in Redux repo, reducer updates a field called isFetching:
case REQUEST_POSTS:
return Object.assign({}, state, {
isFetching: true,
didInvalidate: false
})
case RECEIVE_POSTS:
return Object.assign({}, state, {
isFetching: false,
didInvalidate: false,
items: action.posts,
lastUpdated: action.receivedAt
The component uses connect() from React Redux to subscribe to the store’s state and returns isFetching as part of the mapStateToProps() return value so it is available in the connected component’s props:
function mapStateToProps(state) {
const { selectedReddit, postsByReddit } = state
const {
isFetching,
lastUpdated,
items: posts
} = postsByReddit[selectedReddit] || {
isFetching: true,
items: []
}
return {
selectedReddit,
posts,
isFetching,
lastUpdated
}
}
Finally, the component uses isFetching prop in the render() function to render a “Loading...” label (which could conceivably be a spinner instead):
{isEmpty
? (isFetching ? <h2>Loading...</h2> : <h2>Empty.</h2>)
: <div style={{ opacity: isFetching ? 0.5 : 1 }}>
<Posts posts={posts} />
</div>
}
Even a worse case, what should I do when I have to use the native confirm dialog or alert dialog in redux/react apps? Where should they be put, actions or reducers?
Any side effects (and showing a dialog is most certainly a side effect) do not belong in reducers. Think of reducers as passive “builders of state”. They don’t really “do” things.
If you wish to show an alert, either do this from a component before dispatching an action, or do this from an action creator. By the time an action is dispatched, it is too late to perform side effects in response to it.
For every rule, there is an exception. Sometimes your side effect logic is so complicated you actually want to couple them either to specific action types or to specific reducers. In this case check out advanced projects like Redux Saga and Redux Loop. Only do this when you are comfortable with vanilla Redux and have a real problem of scattered side effects you’d like to make more manageable.
Great answer Dan Abramov!
Just want to add that I was doing more or less exactly that in one of my apps (keeping isFetching as a boolean) and ended up having to make it an integer (which ends up reading as the number of outstanding requests) to support multiple simultaneous requests.
with boolean:
request 1 starts -> spinner on -> request 2 starts -> request 1 ends -> spinner off -> request 2 ends
with integer:
request 1 starts -> spinner on -> request 2 starts -> request 1 ends -> request 2 ends -> spinner off
case REQUEST_POSTS:
return Object.assign({}, state, {
isFetching: state.isFetching + 1,
didInvalidate: false
})
case RECEIVE_POSTS:
return Object.assign({}, state, {
isFetching: state.isFetching - 1,
didInvalidate: false,
items: action.posts,
lastUpdated: action.receivedAt
I'd like to add something. The real world example uses a field isFetching in the store to represent when a collection of items is being fetched. Any collection is generalized to a pagination reducer that can be connected to your components to track the state and show if a collection is loading.
It happened to me that I wanted to fetch details for an specific entity that doesn't fit in the pagination pattern. I wanted to have a state representing if the details are being fetched from the server but also I didn't want to have a reducer just for that.
To solve this I added another generic reducer called fetching. It works in a similar fashion to the pagination reducer and it's responsibility is just to watch a set of actions and generate new state with pairs [entity, isFetching]. That allows to connect the reducer to any component and to know if the app is currently fetching data not just for a collection but for an specific entity.
I didn't happen upon this question until now, but since no answer is accepted I'll throw in my hat. I wrote a tool for this very job: react-loader-factory. It's got slightly more going on than Abramov's solution, but is more modular and convenient, since I didn't want to have to think after I wrote it.
There are four big pieces:
Factory pattern: This allows you to quickly call the same function to set up which states mean "Loading" for your component, and which actions to dispatch. (This assumes that the component is responsible for starting the actions it waits on.) const loaderWrapper = loaderFactory(actionsList, monitoredStates);
Wrapper: The component the Factory produces is a "higher order component" (like what connect() returns in Redux), so that you can just bolt it onto your existing stuff. const LoadingChild = loaderWrapper(ChildComponent);
Action/Reducer interaction: The wrapper checks to see if a reducer it's plugged into contains keywords that tell it not to pass through to the component that needs data. The actions dispatched by the wrapper are expected to produce the associated keywords (the way redux-api-middleware dispatches ACTION_SUCCESS and ACTION_REQUEST, for example). (You could dispatch actions elsewhere and just monitor from the wrapper if you wanted, of course.)
Throbber: The component you want to appear while the data your component depends on isn't ready. I added a little div in there so you can test it out without having to rig it up.
The module itself is independent of redux-api-middleware, but that's what I use it with, so here's some sample code from the README:
A component with a Loader wrapping it:
import React from 'react';
import { myAsyncAction } from '../actions';
import loaderFactory from 'react-loader-factory';
import ChildComponent from './ChildComponent';
const actionsList = [myAsyncAction()];
const monitoredStates = ['ASYNC_REQUEST'];
const loaderWrapper = loaderFactory(actionsList, monitoredStates);
const LoadingChild = loaderWrapper(ChildComponent);
const containingComponent = props => {
// Do whatever you need to do with your usual containing component
const childProps = { someProps: 'props' };
return <LoadingChild { ...childProps } />;
}
A reducer for the Loader to monitor (although you can wire it differently if you want):
export function activeRequests(state = [], action) {
const newState = state.slice();
// regex that tests for an API action string ending with _REQUEST
const reqReg = new RegExp(/^[A-Z]+\_REQUEST$/g);
// regex that tests for a API action string ending with _SUCCESS
const sucReg = new RegExp(/^[A-Z]+\_SUCCESS$/g);
// if a _REQUEST comes in, add it to the activeRequests list
if (reqReg.test(action.type)) {
newState.push(action.type);
}
// if a _SUCCESS comes in, delete its corresponding _REQUEST
if (sucReg.test(action.type)) {
const reqType = action.type.split('_')[0].concat('_REQUEST');
const deleteInd = state.indexOf(reqType);
if (deleteInd !== -1) {
newState.splice(deleteInd, 1);
}
}
return newState;
}
I expect in the near future I'll add things like timeout and error to the module, but the pattern's not going to be very different.
The short answer to your question is:
Tie rendering to rendering code--use a wrapper around the component you need to render with the data like the one I showed above.
Add a reducer that makes the status of requests around the app you might care about easily digestible, so you don't have to think too hard about what is happening.
Events and state aren't really different.
The rest of your intuitions seem correct to me.
Am I the only one thinking that loading indicators don't belong in a Redux store? I mean, I don't think it's part of an application's state per se..
Now, I work with Angular2, and what I do is that I have a "Loading" service which exposes different loading indicators via RxJS BehaviourSubjects.. I guess the mechanism is the same, I just don't store the information in Redux.
Users of the LoadingService just subscribe to those events they want to listen to..
My Redux action creators call the LoadingService whenever things need to change. UX components subscribe to the exposed observables...
You can add change listeners to your stores, using either connect() from React Redux or the low-level store.subscribe() method. You should have the loading indicator in your store, which the store change handler can then check and update the component state. The component then renders the preloader if needed, based on the state.
alert and confirm shouldn't be a problem. They are blocking and alert doesn't even take any input from the user. With confirm, you can set state based on what the user has clicked if the user choice should affect component rendering. If not, you can store the choice as component member variable for later use.
We have three types of notifications in our app, all of which are designed as aspects:
Loading indicator (modal or non-modal based on prop)
Error Popup (modal)
Notification snackbar (non-modal, self closing)
All three of these are at the top level of our app (Main), and wired through Redux as shown in the below code snippet. These props control display of their corresponding aspects.
I designed a proxy that handles all our API calls, thus all isFetching and (api) errors are mediated with actionCreators I import in the proxy. (As an aside, I also use webpack to inject a mock of the backing service for dev so we can work without server dependencies.)
Any other place in the app that needs to provide any type of notification simply imports the appropriate action. Snackbar & Error have params for messages to be displayed.
#connect(
// map state to props
state => ({
isFetching :state.main.get('isFetching'), // ProgressIndicator
notification :state.main.get('notification'), // Snackbar
error :state.main.get('error') // ErrorPopup
}),
// mapDispatchToProps
(dispatch) => { return {
actions: bindActionCreators(actionCreators, dispatch)
}}
)
export default class Main extends React.Component{
I'm saving the urls such as::
isFetching: {
/api/posts/1: true,
api/posts/3: false,
api/search?q=322: true,
}
And then I have a memorised selector (via reselect).
const getIsFetching = createSelector(
state => state.isFetching,
items => items => Object.keys(items).filter(item => items[item] === true).length > 0 ? true : false
);
To make the url unique in case of POST, I pass some variable as query.
And where I want to show an indicator, I simply use the getFetchCount variable

How to model relational data in an Om-like immutable app state

I'm trying to decide whether to use a more traditional Flux implementation or to go with an Om-like structure. I really like the idea of using a single immutable app state object with cursors in javascript, but I'm unsure how to model relational data. I'm looking at using something like Morearty.
My question is how do I avoid duplicating data and deal with nested relational data sent from the server? Let's say I have REST endpoint that gives me inventory and each inventory item has a nested vendor. I also have an endpoint of vendors. I want to have a list of all vendors in my app state, but also reference those vendors on my inventory items. When I make an update to a vendor, I want it to change on all the inventory items that reference that vendor.
Does an Om-like structure work for this kind of application or would a more traditional Flux style app with discreet stores be better?
You probably want to look into something like Redux as your "flux" framework. It's absolutely excellent — we use it at Docker. Here's why it's good and how it can help you.
Why use redux?
It uses a single store model. All stores save state within Redux itself in their own key. An example of the state Redux saves is:
{
vendors: [{...}, ...], // The "vendor" store updates this part of the state
inventory: [...] // the "inventory" store updates this part of the state
}
And Redux, or a redux provider, is the parent of all components. Therefore all components receive the state as props.
How does a single store model improve things?
Each individual "store" which responds to actions within Redux updates only one part of the state object. For example, the "vendor" store only updates the "vendor" key of the state. The individual store is given the current state each time an action happens. This makes stores entirely pure. This is great for testing, immutable data, hot-reloading, rewinds etc.
Because your single top-level Redux store saves all state, each component can receive this state as props and automatically re-render whenever this state changes — even from unrelated components out of hierarchy.
Here's an example of a single store in action, so you can get the idea:
import assign from 'object-assign';
import consts, { metrics, loading, URLS } from 'consts';
const actions = {
// As you can see, each action within a store accepts the current "state"
// and can modify that state by returning new data.
[metrics.FETCH_METRICS_PENDING]: (state, data) => {
return assign({}, state, {status: loading.PENDING});
},
[metrics.FETCH_METRICS_SUCCESS]: (state, data) => {
return assign({}, state, {status: loading.SUCCESS, metrics: data.payload});
},
[metrics.FETCH_METRICS_FAILURE]: (state, data) => {
return assign({}, state, {status: loading.FAILURE});
},
[metrics.OBSERVE_METRICS_DATA]: (state, data) => {
let metrics = state.metrics.slice().concat(data.payload);
return assign({}, state, {metrics});
}
}
// This is the single method that's exported and represents our store.
// It accepts the current state as its primary argument, plus the action
// data as a payload.
//
// This delegates to the methods above depending on `data.type`.
export default function metricStore(state = {}, data) {
if (typeof actions[data.type] === "function") {
return actions[data.type](state, data);
}
return state;
}
How can this model relational data?
Each time data is requested via an action creator it can dispatch multiple actions which update the vendor and inventory state together. This will be reflected in your app within one render.

Categories

Resources