# React & Redux: Lesson1 Managing State ###### tags: `Recat2` [ToC] # Introduction {%youtube zSHjvdQ7nZ8%} Welcome to the course on Redux! There's no doubt that Redux and the Redux ecosystem is a powerful skill to have in your arsenal. However, it is not a topic for beginners and can be a daunting area to break into. We've broken down your path to learning Redux into the following lessons: - Lesson 1 - Managing State - Lesson 2 - UI + Redux - Lesson 3 - Redux Middleware - Lesson 4 - Redux with React - Lesson 5 - Asynchronous Redux - Lesson 6 - react-redux - Lesson 7 - Real World Redux - Lesson 1 - Managing State You’ll learn techniques to make your state more predictable by moving your state to a central location and establishing strict rules for getting, listening, and updating that state. **Lesson 2 - UI + Redux** You’ll learn to move away from having state live in the DOM by creating a vanilla JavaScript application whose state is managed entirely by Redux. **Lesson 3 - Redux Middleware** You’ll learn to create custom middleware and add it to your Redux store. This middleware will allow you to enhance your store by hooking into and intercepting actions before they reach any reducers. **Lesson 4 - Redux with React** You’ll learn how to add React to a Redux application and have the state of that application be managed by Redux. **Lesson 5 - Asynchronous Redux** You’ll learn to better abstract asynchronous events by creating your own custom Thunk middleware and adding it to your store. **Lesson 6 - react-redux** You’ll learn to leverage the react-redux bindings in order to leverage the benefits of a UI library like React and a state management library like Redux. **Lesson 7 - Real World Redux** You’ll take your knowledge of Redux and utilize it by building a real world Redux application. You’ll also learn advanced Redux topics like reducer composition and normalization. **Follow Your Instructors** The Redux ecosystem is changing often! If you want to hear breaking news about Redux, potential changes to its API, conferences and meetups to attend, or just check out some projects, follow your instructors: - [@tylermcginnis](https://twitter.com/tylermcginnis) - [@richardkalehoff](https://twitter.com/richardkalehoff) - [hbkwong](https://www.github.com/hbkwong) # The Store {%youtube A1ZW3cRS65g%} A traditional app might look something like this: ![](https://i.imgur.com/cmz0tpI.png) Notice in the image above, that this simple application has a lot of state: - There are the images in the sidebar on the left. - There are rows of tracks in the main area. - Each Track will have its own information that it's maintaining. - There's the search field at the top that introduces new state to the app (the searched for artist/track information). - And this is just one, simple page of this application. In most sites you use, there is information littered throughout every single page of the entire app. And this is just one, simple page of this application. In most sites you use, there is information littered throughout every single page of the entire app. Remember that the main goal of Redux is to make the state management of an application more predictable. Let's see what that might look like: ![](https://i.imgur.com/g69agU0.png) In this example, the app appears exactly the same to the end user, however, it's functioning quite differently under the hood. All of the data is stored outside of the UI code and is just referenced from the UI code. With a change like this, if the data needs to be modified at all, then all of the data is located in one place and needs to be only changed once. Then the areas of the app that are referencing pieces of data, will be updated since the source they're pulling from has changed. **State Tree** One of the key points of Redux is that all of the data is stored in a single object called the state tree. But what does a state tree actually look like? Good question! Here's an example: ``` { recipes: [ { … }, { … }, { … } ], ingredients: [ { … }, { … }, { … }, { … }, { … }, { … } ], products: [ { … }, { … }, { … }, { … } ] } ``` See how all of the data for this imaginary cooking site is stored in a single object? So all of the state (or "application data") for this site is stored in one, single location. This is what we mean when we say "state tree"...it's just all of the data stored in a single object. Throughout this course, whenever we refer to an application's "state tree", we'll use a triangle to convey this concept. ![](https://i.imgur.com/LlAVa48.png) {%youtube o8cEkLqR7VU%} **Summary** In this lesson, we looked at the data in an application. We saw that in traditional apps, the data is mixed in with the UI and markup. This can lead to hard-to-find bugs where updating the state in one location doesn't update it in every location. We learned that the main goal that Redux is trying to offer is predictable state management. The way that Redux tries to accomplish this is through having a single state tree. This state tree is an object that stores the entire state for an application. Now that all state is stored in one location, we discovered three ways to interact with it: 1. getting the state 2. listening for changes to the state 3. updating the state Then we combine the three items above and the state tree object itself into one unit which we called *the store*. We'll look at creating this store in the next lesson. # Create Store: Getting and Listening In this section, we'll be building the store. If you remember from the previous section, the store has the following information: - the state tree - a way to get the state tree - a way to listen and respond to the state changing - a way to update the state ![](https://i.imgur.com/Y7vnVfC.png) So this is what we're going to do in this lesson - we're going to actually create the store code ourselves, from scratch. In the following video, we'll start with a blank `index.js` file and create a factory function that creates store objects. Then we'll have the store keep track of the state, and we'll write the method to get the state from the store. Pop open your code editor, and let's get started! {%youtube YqmnAPNCxkQ%} [Here's the commit with the changes made in this video.](https://github.com/udacity/reactnd-redux-todos-goals/blob/getting-the-state/index.js) In this screencast, we started building out the `createStore` function. Currently, this factory function: - takes in no arguments - sets up a local (private) variable to hold the state - sets up a `getState()` function - returns an object that publicly exposes the` getState()` function Let's take a look at the `getState() `function Our list of things we need to build for the store is shrinking: the state tree - ~~a way to get the state tree~~ - ~~a way to listen and respond to the state changing~~ - a way to update the state Our next task on the list is to make a way to listen for changes to the state. {%youtube AWOuF_qoEh8%} {%youtube 5jVn0L7nlBA%} [Here's the commit with the changes made in this video.](https://github.com/udacity/reactnd-redux-todos-goals/blob/listening-to-changes/index.js) Keep in mind that ``` const subscribe = (listener) => { listeners.push(listener) return () => { listeners = listeners.filter((l) => l !== listener) } } ``` {%youtube -MtD_RCqKK4%} We've got our first rule! > Only an event can change the state of the store. Ok...well, without knowing what an "event" is, this rule is less than helpful :-\ Fear not, because we're going to look at what events are in this video: {%youtube 4SSkRoVunbI%} When an event takes place in a Redux application, we use a plain JavaScript object to keep track of what the specific event was. This object is called an Action. Let's take another look at an Action: ``` { type: "ADD_PRODUCT_TO_CART" } ``` As you can see, an Action is clearly just a **plain JavaScript object**. What makes this plain JavaScript object special in Redux, is that every Action must have a type property. The purpose of the type property is to let our app (Redux) know exactly what event just took place. This Action tells us that a product was added to the cart. That's incredibly descriptive and quite helpful, isn't it? Now, since an Action is just a regular object, we can include extra data about the event that took place: ``` { type: "ADD_PRODUCT_TO_CART", productId: 17 } ``` In this Action, we're including the `productId` field. Now we know exactly which product was added to the store! One more note to keep in mind as you build your Action objects: it's better practice to pass as little data as possible in each action. That is, prefer passing the index or ID of a product rather than the entire product object itself. **Action Creators** are functions that create/return action objects. For example: ``` const addItem = item => ({ type: ADD_ITEM, item }); // or in ES5: var addItem = function addItem(item) { return { type: ADD_ITEM, item: item }; }; ``` ## Summary In this section, we started creating our store by building out a `createStore()` function. So far, this function keeps track of the state, and provides a method to get the state and one to keep track of listener functions that will be run whenever the state changes. In the next section, we'll add a method to handle updating the state. # Updating State Let's step back one more time and think about what Redux is all about. The whole goal of Redux is to increase predictability: > Redux is a predictable state container for JavaScript apps. With this in mind, let's see dig into how we can use actions and our state tree to predictably manage an application's state. {%youtube 15sTwJsyWbU%} And we've got our second rule! > The function that returns the new state needs to be a pure function. So far, our rules are: 1. Only an event can change the state of the store. 2. The function that returns the new state needs to be a pure function. A [pure function](https://en.wikipedia.org/wiki/Pure_function) can be a bit theoretical, so we'll take it step by step and explain why a pure function is so powerful and how it helps improve predictability. {%youtube o9cWPrOMuyU%} # What are Pure Functions? Pure functions are integral to how state in Redux applications is updated. By definition, pure functions: 1. Return the same result if the same arguments are passed in 2. Depend solely on the arguments passed into them 3. Do not produce side effects, such as API requests and I/O operations Let’s check out an example of a pure function, `square()`: ``` // `square()` is a pure function const square = x => x * x; ``` `square()` is a pure function because it outputs the same value every single time, given that the same argument is passed into it. There is no dependence on any other values to produce that result, and we can safely expect just that result to be returned -- no side effects (more on this in a bit!). On the other hand, let’s check out an example of an impure function, `calculateTip()`: ``` // `calculateTip()` is an impure function const tipPercentage = 0.15; const calculateTip = cost => cost * tipPercentage; ``` `calculateTip()` calculates and returns a number value. However, it relies on a variable (`tipPercentage`) that lives outside the function to produce that value. Since it fails one of the requirements of pure functions, `calculateTip()` is an impure function. However, we could convert this function to a pure function by passing in the outside variable, `tipPercentage`, as a second argument to this function! ``` const calculateTip = (cost, tipPercentage = 0.15) => cost * tipPercentage; ``` # Why Pure Functions Are Great For our purposes, the most important feature of a pure function is that it's predictable. If we have a function that takes in our state and an action that occurred, the function should (if it's pure!) return the exact same result every single time. You're going to be sick of this by the end ;-) but this course (and Redux!) are all about predictability! {%youtube QU_WvPaC6cM%} [Here's the commit with the changes made in this video.](https://github.com/udacity/reactnd-redux-todos-goals/blob/the-reducer-function/index.js) {%youtube z5yJhTOxmMU%} {%youtube wIyRfRSpvDo%} {%youtube P09BK4IXzmk%} [Here's the commit with the changes made in this video.](https://github.com/udacity/reactnd-redux-todos-goals/blob/create-store-dispatch/index.js) The new `dispatch()` method is pretty small, but is vital to our functioning store code. To briefly recap how the method functions: - `dispatch()` is called with an Action - the reducer that was passed to `createStore()` is called with the current state tree and the action...this updates the state tree - because the state has (potentially) changed, all listener functions that have been registered with the `subscribe()` method are called ## Summary In this section, we learned about a number of important points about Redux. We learned about pure functions, a Reducer function (which, itself, needs to be a pure function), dispatching changes in our store, and identifying which parts of our code are generic library code and which are specific to our app. ## Shannnon Note Redux可以用來儲存資料,簡單來說就是把畫面以及資料分開比較好管理,store就是存放資料的地方 Store裡面有state以及listeners,State是資料的狀態listeners是要針對State做的動作 但是使用Redux有幾個重要的規則 1. 只有event可以改變state的內容,那我認為就是只有Action可以呼叫subscribe將要執行的動作放進去 2. reducer限定pure function ### Pure Function 定義 1. 對於相同的input output永遠相同,針對function裡面的變數不會永遠修改 3. 只使用local variable不使用global variable 4. 不會產生side effects: 表示這個fumction不會跟外界任何function or items or variable 有任何interaction,or network requewsts or mutating external state pure 核心就在於可以完全自給自足又不會受到他人影響,雖然不是說所有的impure functions are bad, but we know that the pure function is predictable. 有一些好處: 就如剛剛所說我們會傳入listener進去store去對State做一些操作,但是我們希望的是這個listener也就是function能夠每次不管傳入甚麼東西回傳的內容都能夠抱持一致 ### Reducer 從上面來看Redux需要三個元件: action, reducer, createStore() - action 就是一些觸發的關鍵動作,裡面必須要有type來描述該動作 - reducer 就是一個用來針對State做改變的方法,需要傳入action and state來做一些邏輯,但是reducer必須是pure function - createStore 目前看來就是儲存資料的地方,可呼叫`getState()`方法取得目前最新的State狀態,或是儲存一些listeners進去, createStore有以下要素 - state tree - getting the state - listening for changes - updating the state ### Updating the state 通常在createStore裡面,要讓state做更新(`dispatch()`)需要以下元件 1. state: 本來就建立在CreatStore 2. action: 透過呼叫`dispatch(action)`傳入的方法 3. listeners foreach function: 當資料有做更動的時候就要重新呼叫一次所有監聽事件,我在猜可能為了讓使用這知道state做改變所設計的listener ### Sum up 其實剛剛做的createStore()是可以透過npm去取得這個library code, 但是我們必須要做的是做reducer並且將reducer傳入createStore裡面。 # Putting it all together {%youtube HEQR3KYjG24%} [Here's the commit with the changes made in this video.](https://github.com/udacity/reactnd-redux-todos-goals/blob/putting-it-all-together/index.js) ![](https://i.imgur.com/Bjadfas.png) We've finally finished creating the `createStore` function! Using the image above as a guide, let's break down what we've accomplished: - we created a function called `createStore()` that returns a store object - `createStore()` must be passed a "reducer" function when invoked - the store object has three methods on it: - `getState()` - used to get the current state from the store - `subscribe()` - used to provide a listener function the store will call when the state changes - `dispatch()` - used to make changes to the store's state - the store object's methods have access to the state of the store via closure ## Summary Up until this point, we've been building out the `createStore()` function, piece by piece. In this section, we put all of those pieces together to create a fully functioning project. Then we took that code and demoed it working in the console. We showed that subscribing to the store returned a function we could use to unsubscribe later. We also dispatched an action and saw how the state was updated as a result. In the next section, we'll keep building up our app-specific parts of the code to handle different actions and to be more error-proof. ## Shannon Note 簡單來說重新申名createStore裡面有甚麼 1. createStore必需要傳入reducer, 就是改變State狀態的方法 2. getState來取得目前state的狀態 3. subscribe提供listener function給store當state改變的時候就會call的方法 4. dispatch就是提供action # Managing more State As of right now, our code is handling the `ADD_TODO` action. There are still a couple more actions that our app is supposed to be able to handle: - the `REMOVE_TODO` action - the `TOGGLE_TODO` action {%youtube a3giVoHKkHE%} {%youtube Yqeks3OSY6M%} [Here's the commit with the changes made in this video.](https://github.com/udacity/reactnd-redux-todos-goals/blob/more-actions/index.js) # Recap of New Actions Our app can not only handle adding todo items -- it can now handle removing a todo item, as well as toggling a todo item (as complete or incomplete)! To make this all possible, we updated our `todos` reducer to be able to respond to actions of the `type` `REMOVE_TODO` and `TOGGLE_TODO`. Before moving on, let's make sure we're on the same page on how this was all implemented. Our `todos` reducer originally looked like the following: ```javascript function todos (state = [], action) { if (action.type === 'ADD_TODO') { return state.concat([action.todo]); } return state; } ``` To resolve additional action types, we added a few more conditions to our reducer logic: ```javascript function todos (state = [], action) { if (action.type === 'ADD_TODO') { return state.concat([action.todo]); } else if (action.type === 'REMOVE_TODO') { // ... } else if (action.type === 'TOGGLE_TODO') { // ... } else { return state; } } ``` Note that just like the original `todos` reducer, we simply return the original state if the reducer receives an action type that it's not concerned with. To remove a todo item, we called `filter()` on the state. This returns a new state (an array) with only todo items whose `id`'s do not match the `id` of the todo we want to remove: ```javascript function todos (state = [], action) { if (action.type === 'ADD_TODO') { return state.concat([action.todo]); } else if (action.type === 'REMOVE_TODO') { return state.filter((todo) => todo.id !== action.id); } else if (action.type === 'TOGGLE_TODO') { // ... } else { return state; } } ``` To handle toggling a todo item, we want to change the value of the `complete` property on whatever `id` is passed along on the action. We mapped over the entire state, and if `todo.id` matched `action.id`, we used `Object.assign()` to return a new object with merged properties: ```javascript function todos (state = [], action) { if (action.type === 'ADD_TODO') { return state.concat([action.todo]); } else if (action.type === 'REMOVE_TODO') { return state.filter((todo) => todo.id !== action.id); } else if (action.type === 'TOGGLE_TODO') { return state.map((todo) => todo.id !== action.id ? todo : Object.assign({}, todo, { complete: !todo.complete })); } else { return state; } } ``` We then refactored our entire `todos` reducer to use a `switch` statement rather than multiple `if/else` statements: ```javascript function todos (state = [], action) { switch(action.type) { case 'ADD_TODO' : return state.concat([action.todo]); case 'REMOVE_TODO' : return state.filter((todo) => todo.id !== action.id); case 'TOGGLE_TODO' : return state.map((todo) => todo.id !== action.id ? todo : Object.assign({}, todo, { complete: !todo.complete })); default : return state; } } ``` In the above snippet, we matched `cases` against an expression (i.e., `action.type`), and executed statements associated with that particular `case`. Let's now extend our app with some additional functionality! ## Adding Goals to our App Currently, the app keeps track of a single piece of state - a list of todo items. Let's make the app a bit more complicated and add in a second piece of state for our app to track - goals. {%youtube kPYmzsY2RAo%} [Here's the commit with the changes made in this video.](https://github.com/udacity/reactnd-redux-todos-goals/blob/goals-reducer/index.js I kind of pointed it out at the end of the previous screencast, but we now have two reducer functions: - todos - goals However, the createStore() function we built can only handle a single reducer function: ```javascript // createStore takes one reducer function as an argument const store = createStore(todos); // We can't call createStore() passing it two reducer functions: // this will not work const store = createStore(todos, goals); So we've got a problem... ``` So we've got a problem... {%youtube QTNV7BP7dWs%} {%youtube qL0HB_kmiQ0%} [Here's the commit with the changes made in this video.](https://github.com/udacity/reactnd-redux-todos-goals/blob/combine-reducers/index.js) Whenever `dispatch` is called, we invoke our `app` function. The `app` function will then invoke the `todos` reducer as well as the `goals` reducer. Those will return their specific portions of the state. And then, the `app` function will return a state object with a `todos` property (the value of which is what the todos reducer returned) and a `goals` property (the value of which is what the goals reducer returned). ```javascript function todos (state = [], action) { switch(action.type) { case 'ADD_TODO' : return state.concat([action.todo]) case 'REMOVE_TODO' : return state.filter((todo) => todo.id !== action.id) case 'TOGGLE_TODO' : return state.map((todo) => todo.id !== action.id ? todo : Object.assign({}, todo, { complete: !todo.complete })) default : return state } } function goals (state = [], action) { switch(action.type) { case 'ADD_GOAL' : return state.concat([action.goal]) case 'REMOVE_GOAL' : return state.filter((goal) => goal.id !== action.id) default : return state } } function app (state = {}, action) { return { todos: todos(state.todos, action), goals: goals(state.goals, action), } } /* Passing the root reducer to our store since our createStore function can only take one reducer. */ const store = createStore(app); ``` ## Summary In this section, we bolstered our application to handle a number of different actions as well as an entirely new piece of state! In addition to our app handling the ADD_TODO action, it now handles: - the REMOVE_TODO action - the TOGGLE_TODO action We also created the goals reducer which handles: - an ADD_GOAL action - a REMOVE_GOAL action So our application can now manage the state of our todos and goals, and it can do all of this, predictably! In the next and final section of this lesson, we'll look at how we can convert some of our existing functionality to follow best practices. ## Shannon Note 前面幾張你會發現我們都只有一個reducer但是在真實生活中一定還需要製作更多reducer但是很不幸的,createStore()一次只能放一個reducer,因此我們需要製作一個app function他會回傳一個物件,這個物件的屬性就是所有我們做的reducer如下 ```javascript //你會發現這個app裝的是所有reducer的結果,有個是空字串有個可能會有State change結果但是整體來說app function return an object function app (state={}, action){ return ( { todo: todo(state, action), goal: goal(state, action) //如果action不會用到這個reducer就return [] }) } ``` # Better Practices {%youtube BnX0BPQPuY4%} [Here's the commit with the changes made in this video.](https://github.com/udacity/reactnd-redux-todos-goals/blob/constants/index.js) ## Summary In this section, we converted our actions to use JavaScript constants instead of strings. We also refactored our `.dispatch()` calls from passing in unique objects directly to them, to calling special functions that create the action objects - these special functions that create action objects are called **Action Creators**. ## Shannon note 這節有兩個重點會讓程式變得更好 1. 像是action的type可以做成global變數這樣比較不易打錯字 2. 建立action creators簡單來說就是在call dispatch時不用一直重寫action object可以透過呼叫function的方式來建立object # Lesson Summary {%youtube a4hs3EgcyeI%} ## Lesson Challenge Answer the following questions and share your answers with your classmates. 1) How is state updated in Redux applications? 2) What is the store? Here's how what you've accomplished in the program thus far maps to your career: ![](https://i.imgur.com/tpzSd7y.png) ## shannon note 回答上面問題 1. 如果要讓State update需要做幾件事 1. 建立reducer 傳進去CreateStore作為改變state需要執行的動作 2. 透過createStore的dispatch function, 這個function will call the reducer we passed in. 然後when we call dispath, we need to pass the action into it. so the dispatch will use the input pass it into the reducer we made to reture the result by listeners. 3. reducer 需要兩件東西傳入,State and action, state是資料狀態 action是目前觸發了哪些行為我們希望更新的內容也在action內