Say I have two components, App and DialogBox;
so my react store also has two objects
const rootReducer = combineReducers({
app: appReducer,
dialog: dialogReducer
});
export const store = createStore(rootReducer);
Now, when I have to open the Dialog box, I call the dialogBoxActions.openDialog()
And, when I close the Dialog box, I call dialogBoxActions.closeDialog() and also appActions.notifyDialogClosed();
This works, but is there a way to do this in more clearer way?
For example can I use the state.dialog from the store in App? This is what I tried in the App
const mapStateToProps = (state) => {
return {
checkCloseDialog: state.dialog.openDialog
}
}
The ComponentWillReceiveProps does get the checkCloseDialog object, but it gets the old state. I debugged to find out that it gets triggered correctly after the reducer function of the DialogBox component but I get old data.
Is there any elegant way of accessing each other's store or is it Redux's philosophy that components should communicate with each other via actions?
Ah! Okay, my bad.
Yes, it is possible. And it works. My mistake was I was trying to use it inside ComponentWillReceiveProps() method. The thing is, it seems the redux store gets updated later. So, ComponentWillReceiveProps() will hold an old state.
Hence, render() and ComponentDidUpdate() methods get the updated state.
The aim here is to help reduce the multiple action calls between independent components.
For example, a Dialog should not be concerned with what the caller should do after it closes itself. Instead the caller should subscribe to the state of Dialog to decide what to do.
Hope this helps others.
I am new to React and want to understand the difference from classic MVC.
I want to create a simple components that loads some data initially and renders let say a grid.
On some state or prop change it will reload the data and re-render.
What is the best approach in react from below two options?
using the lifecycle events to load the data, update some state and render while in another event will show some loading opacity.
Work with redux and react-redux? but in all example I cant see API calls.
Is this the role of a middleware (Thunk?)?
Will appropriate an explanation.
Both the approaches are correct. It depends on your use case. If you can avoid using redux in your app, use the lifecycle methods to make API calls (also called subscriptions in react documentation). If you think your app has many components and different components needs to share a state, then use redux.
You should also look at React hooks https://reactjs.org/docs/hooks-reference.html
You can use Effect Hook https://reactjs.org/docs/hooks-effect.html to make API calls and update your component's state.
Update:
Both Thunk and Sage are used to manage side effects in your application (making API calls from here). I've used saga, I don't know much about thunk.
How you would use redux-saga:
Say if you want to get some data for display on a button click, this is how it works:
On button click you dispatch an action, say GET_DATA
Your redux reducer will change some state on this particular action, say isLoading=true
You can use isLoading in your component to show a spinner/overlay
At the same time saga will listen to GET_DATA action and make the API call
If success, from Saga you'll dispatch an action, say GET_DATA_SUCCESS with the data from API call
Reducer will change isLoading=false and set the data in state, say apiData = { ... }
If failure, from Saga you'll dispatch an action, say GET_DATA_FAILED with the error code/message
Reducer will change isLoading=false and set the error in state, say apiError = { ... }
You can now use isLoading=false in you component to remove spinner and display the data (apiData) or error (apiError) in you component.
You can go through this tutorial to learn more https://redux-saga.js.org/docs/introduction/BeginnerTutorial.html
I'm trying to restructure my React app around Redux, and getting confused.
one site says:
Components receive props from their parent. These props should not be
modified inside the component.
another says:
You used to be able to change props with setProps and replaceProps but
these have been deprecated. During a component’s life cycle props
should not change (consider them immutable).
So I should consider props to be immutable over a component's lifetime. OK, then, the alternative is state (React state, which I understand is a very different thing from Redux state. OK...)
React state is the mutable state of the component. It belongs to the component and, unlike props, can change during the component's life cycle. OK. Outside of the context of Redux, I totally get that. Each component owns its own mutable state and passes what it needs to down to its children as props, which are for the children immutable. When the state changes it will cause the component to re-render its children, giving them new props as needed.
Now introduce Redux. The Redux store holds a single state for the entire app. Does this now replace any React state? Are all of the elements of the Redux state delivered to React components as props? There is mapStateToProps which seems to suggest this. So can I now forget about React state entirely?
I think of the component life cycle as lasting, say, as long as the user can see the thing on screen. But if props can't change during the component life cycle, and everything is now (from the React perspective) props - does that mean the life cycle is only as long as it takes Redux to refresh its store?
As pointed out by Dan Abramov in his famous You might not need redux article,
Redux should be used only when you need persistence across the application. When data is ephemeral (think about a selected accordion opened in a simple view) you can safely store your state in a local state.
So:
Does this now (Redux) replace any React state?
If you wish, yes. But it isn't mandatory.
Are all of the elements of the Redux state delivered to React components as props?
Usually, yes.
So can I now forget about React state entirely?
Yes, you can. Although is not mandatory and local/redux state can live happily together.
[...] does that mean the life cycle is only as long as it takes Redux to refresh its store?
Renders occurs as any state (local or redux) changes.
Lucy Bain's post is good as an explainer, but note that the mentions of setProps refer to a very old version of React, and the linked release notes for React 0.14 describe those as only having been useful for updating the top-level component in an app. Today, if you actually need to update the props for the root component, you'd call ReactDOM.render(<MyApp newProp={123} />) a second time.
See the Redux FAQ entry on splitting state between Redux and React components for some rules of thumb to help decide where each piece of state should live.
I'd also encourage you to read through the React-Redux documentation as well to understand how they fit together.
I think what you need to understand first is the Redux cycle which goes like this:
Action Creator->Action->dispatch->Reducers->State
An Action Creator is a function that will return a plain JavaScript object referred to as the Action. The action will have a type and payload that provides context on what its doing, for example, creating an insurance policy, it would look like so:
// People dropping off a form (Action Creator)
const createPolicy = (name, amount) => {
return { // Action(a form in this analogy)
type: 'CREATE_POLICY',
payload: {
name: name,
amount: amount
}
};
};
All action creators look identical to what you see what here with some small variation.
But I know the question is understanding the cycle and their purposes not so much the syntax.
So you want to understand what role your action creators need to play in your React app and then on to your Reducers and figuring out their role, but really after you've figured out the hard part, the Action Creators, the rest start to fall into place.
The dispatch receives the action and dispatches it out to all the different Reducers. dispatch is part of the Redux library itself, so you don't need to write it out and your question regarding mapStateToProps if you tie it in with the higher order component utilizing the connect() helper, that whole process is like a manual override of the dispatch process.
The idea behind Reducers is that we write out functions and each one models a different behavior inside the application. Each reducer gets called with an Action, the reducer inspects the action and determines whether to modify behavior based on that action.
So action creators are like people filing an insurance claim and the action is the actual claim. Those claims go to these different departments called reducers.
So keeping to this analogy of insurance claims, a reducer I may want to create would be a claims history reducer like so:
// Reducers (Departments)
const claimsHistory = (oldListOfClaims, action) => {
};
So the reducers' job is to determine whether or not it cares about the type of action it is receiving. If it is of type createClaim then we want to ensure the code inside the function pulls off the payload property and add it to the list of claims, if not then return list of claims unchanged and that would look like so:
// Reducers (Departments)
const claimsHistory = (oldListOfClaims, action) => {
if(action.type === 'CREATE_CLAIM') {
// we care about the action (claim)
return [...oldListOfClaims, action.payload]
}
// we dont care about the action (form)
};
So I am just using ES2015 syntax which takes an array, take all of the records and add them to a brand new array and add the new record of action.payload.
Its the equivalent to doing const numbers = [1,2,3] and then [...numbers, 4] which outputs:
(4) [1, 2, 3, 4]
Or the equivalent of oldListOfClaims.push(action.payload); but there is a distinct difference between this one and the one I used.
In the syntax I used for the reducer I am creating a brand new array and adding records to it, whereas the push() method is modifying an existing array.
We always want to avoid modifying existing data structures inside a reducer. You will never see push() inside a reducer.
Then there is the case where we don't care about the action:
// Reducers (Departments)
const claimsHistory = (oldListOfClaims, action) => {
if(action.type === 'CREATE_CLAIM') {
// we care about the action (claim)
return [...oldListOfClaims, action.payload]
}
// we dont care about the action (form)
return oldListOfClaims;
};
Next, you need to handle the case where the very first time a reducer gets called, there will be no data to passed to it. We will essentially receive the value of undefined. We need to default the value of the first argument.
// Reducers (Departments)
const claimsHistory = (oldListOfClaims = [], action) => {
if(action.type === 'CREATE_CLAIM') {
// we care about the action (claim)
return [...oldListOfClaims, action.payload]
}
// we dont care about the action (form)
return oldListOfClaims;
};
So undefined got replaced with an empty array.
So figuring out your actions and reducers is the hard part when implementing Redux as well as the fine grain rules that go with it such as making sure you don't modify the array that gets passed in and ensuring you always return some value.
The overall goal of a reducer is to take some existing data passed to it as an action and find and return that existing data based upon the contents of an action.
So this is all the baseline stuff to get you started. Out of all this is where you want to return a new instance of a Redux store.
Depending on your project you may have 1 to 3 or more action creators and reducers. You then wire all these up into a single object called a store which is just an assembly of different actions and reducers.
So somewhere in your components you are going to be adding something like this: const { createStore, combineReducer } = Redux;
This is what your combineReducers looks like:
const ourDepartments = combineReducers({
accounting: accounting,
claimsHistory: claimsHistory,
policies: policies
});
Then you complete creating your store like so:
const store = createStore(ourDepartments);
store;
The store represents the entire Redux application. It contains references to all your different reducers and to all your state produced by those reducers or data.
The store object has some useful functions such as the dispatch function. In order to call dispatch() we have to pass in an action created by an action creator and then pass it off to store.dispatch().
const store = createStore(ourDepartments);
const action = createPolicy('Alejandra', 35);
console.log(action);
store.dispatch();
When you console log out action you should see you have the type of action and your payload:
{type: "CREATE_POLICY", payload: {…}}
You then take that action and pass it to store.dispatch() like so:
So far you probably saw an error saying that Action must be a plain JavaScript object and so on, well this error will go away once you pass in the action like so:
store.dispatch(action);
You can check to see the state of the application print out like so:
console.log(store.getState());
{accounting: 135, claimsHistory: Array(0), policies: Array(1)}
accounting: 135
claimsHistory: []
policies: ["Alejandra"]
proto: Object
Now you can start passing in the actions like so:
store.dispatch(createPolicy('Alejandra', 35));
store.dispatch(createPolicy('Ben', 20));
store.dispatch(createPolicy('Daniel', 78));
store.dispatch(createClaim('Alejandra', 120));
store.dispatch(createClaim('Ben', 50));
store.dispatch(deletePolicy('Daniel'));
console.log(store.getState());
Output in console:
{accounting: 63, claimsHistory: Array(2), policies: Array(2)}
accounting: 63 claimsHistory: Array(2) 0: {name: "Alejandra",
amountOfMoneyToCollect: 120} 1: {name: "Ben", amountOfMoneyToCollect:
50} length: 2
proto: Array(0) policies: Array(2) 0: "Alejandra" 1: "Ben" length: 2
proto: Array(0)
proto: Object
So thats how the Redux library works
Alright, so I showed you how it works on its own, but how do the two libraries, Redux and React interact with each other?
So yes, with the implementation of Redux, we make use of component level state, much less frequently. We generally store all of our data inside of Redux instead.
There are some scenarios where we want to have state inside of both Redux and React components, but in general all your state inside of React app is now inside of Redux instead, so that translates into a much more simple React application.
Lets say you had some iTunes type of app, how would we put it together with just Reactjs?
So this would be your app without redux:
So here your SongDetail only needs to know what your currently selected song is. So your App component passes it down to SongDetail as a prop and the SongDetail would render it out to the screen.
With React alone this would be a simple and straightforward application.
So how does your application change with Redux?
So just as before, we are going to have an App component, a SongList and SongDetail, but the App component is going to be passing down very little information down the SongList and SongDetail.
Instead, we are going to abstract out these ideas of creating a list of songs and selecting a song and what the currently selected song is into a redux application.
So you would have a reducer that will produce a list of songs and a reducer that records what a currently selected song is. Those are the two pieces of state in your application.
Finally, we are going to ensure we have an action creator to somehow change your state. That is the only way you change your state inside a redux app.
So your action creator may be called select song, that will dispatch an action and tell the selected song reducer to update its data and reflect the new currently selected song.
So thats your app with Redux.
I'm working with react-redux in a current project and on my search for react wisdom I came across a 'convention' I don't understand.
Other programmers tend to put a prop to state right in the constructor. Why is that?
I've never seen it in the official documentation and from there I learned that these two are two different things to hold and share data.
Is there any use in it or mybe just a personal preference?
Thanks.
It sounds like a pattern for when you need some initial value from outside the component, but then want to either ignore, or not immediately affect, the outside (by for example dispatching Redux actions on each value change).
For example, maybe you have a form that should be prefilled with some values that the component gets from the outside (Redux state) through mapStateToProps or similar. You could let every form fields onChange dispatch an action that changes the Redux state, causes the incoming props to change, and then re-render the form with the new value. Or maybe you find that to be overkill. Maybe you're satisfied with keeping the changing form data in the component internal state until the user actually submits the form, and only then dispatch an action that will post the form data, change the Redux state and pass new props (like success status) down to the form component.
Yes, it is possible, especially when you want to keep things simple by not using container components or flux / redux store to manage application's state.
A component can manage its own state, and the initialState will be assigned as the props passed from its parent component.
Consider the following example:
class TodoList extends React.Component {
constructor(props) {
// Assign todos property to state so that the TodoList component
// can self-manage this value without interacting with outside components.
this.setState({ todos: props.todos });
}
...
addTodo(todoDescription) {
this.setState({ todos: this.state.todos.concat[todoDescription] });
}
}
However, I still do recommend to separate the view components and data manipulating components when your applications is complex.
I came across interesting article (Link at the end of post). Author of the article states, that they treat redux store as client-side database and UI logic does not fit there (If it's not needed for unrelated components), even for data fetching purposes. For example we want to show some loading spinner when fetching some data:
async componentDidMount() {
this.setState({isLoading: true});
await this.props.fetchSomeData();
this.setState({isLoading: false});
}
We fire async thunk action that fetches some data that is needed for more than one component or we want to cache that data, even when that component is unmounted.
The only component that is concerned about loading state is the one in which we fire thunk action, other components are not concerned about state of loading. But I always see redux examples with async actions creators which fire REQUEST/SUCCESS/FAILURE actions types and reducers bloated with loading states even if they're are used in one component.
I can see some cons of this code, that some state lives in component and some in redux, but pros are that redux store is not bloated with state that is not needed for other components, also we can avoid the verbosity of redux.
So my question would be, what are cons of this state separation regarding this particular example ?
Article: https://dev.bleacherreport.com/3-things-i-learned-about-working-with-data-in-redux-5fa0d5f89c8b
(Also interesting discussion in article comments)
IMO, some of the cons of your state separation approach is that:
If there is complex API fetching behind the scene ( fetch A then fetch B then fetch C) redux-thunk would be inadequate and you will have to use redux-saga or redux-observable and together with them, the REQUEST/SUCCESS/FAILURE styles.
If, in the future, there are other components need to listen to the 'loading' state or worse need the same data but there is no guarantee that the order of display them will be the same ( A mount and fetch data and then B mount before the data is fetched, and B fetch again ), you will have to refactor all the structures. And that doesn't count the case when you need to CANCEL a fetch.
If your component unmount before the data is fetched, the "this.setState" call will result in a null reference error.
In general, I agree that UI state should be put in the component, but global state must always be stored in the Redux store and your isLoading is not totally a UI state but a state of the request that your component are listening to.