简体   繁体   中英

What is the life cycle of a React component in context of Redux?

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?

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM