115 Matching Annotations
  1. Dec 2022
  2. Sep 2022
  3. Jan 2022
  4. Dec 2021
    1. Globalize Selectors if Needed​

      [...]

      We refer to this pattern as "globalizing" selectors. A "globalized" selector is one that accepts the Redux root state as an argument, and knows how to find the relevant slice of state to perform the real logic. A "localized" selector is one that expects just a piece of the state as an argument, without knowing or caring where that is in the root state:

      // "Globalized" - accepts root state, knows to find data at `state.todos`
      const selectAllTodosCompletedGlobalized = state =>
        state.todos.every(todo => todo.completed)
      
      // "Localized" - only accepts `todos` as argument, doesn't know where that came from
      const selectAllTodosCompletedLocalized = todos =>
        todos.every(todo => todo.completed)
      

      "Localized" selectors can be turned into "globalized" selectors by wrapping them in a function that knows how to retrieve the right slice of state and pass it onwards.

      Redux Toolkit's createEntityAdapter API is an example of this pattern. If you call todosAdapter.getSelectors(), with no argument, it returns a set of "localized" selectors that expect the entity slice state as their argument. If you call todosAdapter.getSelectors(state => state.todos), it returns a set of "globalized" selectors that expect to be called with the Redux root state as their argument.

      There may also be other benefits to having "localized" versions of selectors as well. For example, say we have an advanced scenario of keeping multiple copies of createEntityAdapter data nested in the store, such as a chatRoomsAdapter that tracks rooms, and each room definition then has a chatMessagesAdapter state to store the messages. We can't directly look up the messages for each room - we first have to retrieve the room object, then select the messages out of that. This is easier if we have a set of "localized" selectors for the messages.

    2. Balance Selector Usage​

      [...]

      Similarly, don't make every single selector memoized!. Memoization is only needed if you are truly deriving results, and if the derived results would likely create new references every time. A selector function that does a direct lookup and return of a value should be a plain function, not memoized.

      Some examples of when and when not to memoize:

      // ❌ DO NOT memoize: will always return a consistent reference
      const selectTodos = state => state.todos
      const selectNestedValue = state => state.some.deeply.nested.field
      const selectTodoById = (state, todoId) => state.todos[todoId]
      
      // ❌ DO NOT memoize: deriving data, but will return a consistent result
      const selectItemsTotal = state => {
        return state.items.reduce((result, item) => {
          return result + item.total
        }, 0)
      }
      const selectAllCompleted = state => state.todos.every(todo => todo.completed)
      
      // ✅ SHOULD memoize: returns new references when called
      const selectTodoDescriptions = state => state.todos.map(todo => todo.text)
      
    3. Creating Unique Selector Instances​

      There are many cases where a selector function needs to be reused across multiple components. If the components will all be calling the selector with different arguments, it will break memoization - the selector never sees the same arguments multiple times in a row, and thus can never return a cached value.

      The standard approach here is to create a unique instance of a memoized selector in the component, and then use that with useSelector. That allows each component to consistently pass the same arguments to its own selector instance, and that selector can correctly memoize the results.

      For function components, this is normally done with useMemo or useCallback:

      import { makeSelectItemsByCategory } from './categoriesSlice'
      
      function CategoryList({ category }) {
        // Create a new memoized selector, for each component instance, on mount
        const selectItemsByCategory = useMemo(makeSelectItemsByCategory, [])
      
        const itemsByCategory = useSelector(state =>
          selectItemsByCategory(state, category)
        )
      }
      
    4. Using Selectors with React-Redux​

      • Calling Selectors with Parameters​

      It's common to want to pass additional arguments to a selector function. However, useSelector always calls the provided selector function with one argument - the Redux root state.

      The simplest solution is to pass an anonymous selector to useSelector, and then immediately call the real selector with both state and any additional arguments:

      import { selectTodoById } from './todosSlice'
      
      function TodoListitem({ todoId }) {
        // Captures `todoId` from scope, gets `state` as an arg, and forwards both
        // to the actual selector function to extract the result
        const todo = useSelector(state => selectTodoById(state, todoId))
      }
      
    5. Selector Factories​

      createSelector only has a default cache size of 1, and this is per each unique instance of a selector. This creates problems when a single selector function needs to get reused in multiple places with differing inputs.

      One option is to create a "selector factory" - a function that runs createSelector() and generates a new unique selector instance every time it's called:

      const makeSelectItemsByCategory = () => {
        const selectItemsByCategory = createSelector(
          [state => state.items, (state, category) => category],
          (items, category) => items.filter(item => item.category === category)
        )
        return selectItemsByCategory
      }
      

      This is particularly useful when multiple similar UI components need to derive different subsets of the data based on props.

    6. Passing Input Parameters​

      A Reselect-generated selector function can be called with as many arguments as you want: selectThings(a, b, c, d, e). However, what matters for re-running the output is not the number of arguments, or whether the arguments themselves have changed to be new references. Instead, it's about the "input selectors" that were defined, and whether their results have changed. Similarly, the arguments for the "output selector" are solely based on what the input selectors return.

      This means that if you want to pass additional parameters through to the output selector, you must define input selectors that extract those values from the original selector arguments:

      const selectItemsByCategory = createSelector(
        [
          // Usual first input - extract value from `state`
          state => state.items,
          // Take the second arg, `category`, and forward to the output selector
          (state, category) => category
        ],
        // Output selector gets (`items, category)` as args
        (items, category) => items.filter(item => item.category === category)
      )
      

      For consistency, you may want to consider passing additional parameters to a selector as a single object, such as selectThings(state, otherArgs), and then extracting values from the otherArgs object.

    7. Reselect Usage Patterns and Limitations​

      Nesting Selectors​

      It's possible to take selectors generated with createSelector, and use them as inputs for other selectors as well. In this example, the selectCompletedTodos selector is used as an input to selectCompletedTodoDescriptions:

      const selectTodos = state => state.todos

      const selectCompletedTodos = createSelector([selectTodos], todos => todos.filter(todo => todo.completed) )

      const selectCompletedTodoDescriptions = createSelector( [selectCompletedTodos], completedTodos => completedTodos.map(todo => todo.text) )

    8. Because of this, it's important that all of the "input selectors" you provide should accept the same types of parameters. Otherwise, the selectors will break.

      const selectItems = state => state.items;
      
      // expects a number as the second argument
      const selectItemId = (state, itemId) => itemId;
      
      // expects an object as the second argument
      const selectOtherField (state, someObject) => someObject.someField;
      
      const selectItemById = createSelector(
          [selectItems, selectItemId, selectOtherField],
          (items, itemId, someField) => items[itemId]
      );
      

      In this example, selectItemId expects that its second argument will be some simple value, while selectOtherField expects that the second argument is an object. If you call selectItemById(state, 42), selectOtherField will break because it's trying to access 42.someField.

    9. Also, you can pass multiple arguments into a selector. Reselect will call all of the input selectors with those exact inputs:
      const selectItems = state => state.items
      const selectItemId = (state, itemId) => itemId
      
      const selectItemById = createSelector(
        [selectItems, selectItemId],
        (items, itemId) => items[itemId]
      )
      
      const item = selectItemById(state, 42)
      
      /*
      Internally, Reselect does something like this:
      
      const firstArg = selectItems(state, 42);  
      const secondArg = selectItemId(state, 42);  
      
      const result = outputSelector(firstArg, secondArg);  
      return result;  
      */
      
    10. It's important to note that by default, createSelector only memoizes the most recent set of parameters. That means that if you call a selector repeatedly with different inputs, it will still return a result, but it will have to keep re-running the output selector to produce the result:
      const a = someSelector(state, 1) // first call, not memoized
      const b = someSelector(state, 1) // same inputs, memoized
      const c = someSelector(state, 2) // different inputs, not memoized
      const d = someSelector(state, 1) // different inputs from last time, not memoized
      
    11. In typical Reselect usage, you write your top-level "input selectors" as plain functions, and use createSelector to create memoized selectors that look up nested values:
      const state = {
        a: {
          first: 5
        },
        b: 10
      }
      
      const selectA = state => state.a
      const selectB = state => state.b
      
      const selectA1 = createSelector([selectA], a => a.first)
      
      const selectResult = createSelector([selectA1, selectB], (a1, b) => {
        console.log('Output selector running')
        return a1 + b
      })
      
      const result = selectResult(state)
      // Log: "Output selector running"
      console.log(result)
      // 15
      
      const secondResult = selectResult(state)
      // No log output
      console.log(secondResult)
      // 15
      

      Note that the second time we called selectResult, the "output selector" didn't execute. Because the results of selectA1 and selectB were the same as the first call, selectResult was able to return the memoized result from the first call.

    12. Any "output selector" that just returns its inputs is incorrect! The output selector should always have the transformation logic.Similarly, a memoized selector should never use state => state as an input! That will force the selector to always recalculate.
    13. A somewhat common mistake is to write an "input selector" that extracts a value or does some derivation, and an "output selector" that just returns its result:
      // ❌ BROKEN: this will not memoize correctly, and does nothing useful!
      const brokenSelector = createSelector(
        state => state.todos,
        todos => todos
      )
      
    14. When you call the selector, Reselect will run your input selectors with all of the arguments you gave, and looks at the returned values. If any of the results are === different than before, it will re-run the output selector, and pass in those results as the arguments. If all of the results are the same as the last time, it will skip re-running the output selector, and just return the cached final result from before.

      This means that "input selectors" should usually just extract and return values, and the "output selector" should do the transformation work.

    15. createSelector can accept multiple input selectors, which can be provided as separate arguments or as an array. The results from all the input selectors are provided as separate arguments to the output selector:
      const selectA = state => state.a
      const selectB = state => state.b
      const selectC = state => state.c
      
      const selectABC = createSelector([selectA, selectB, selectC], (a, b, c) => {
        // do something with a, b, and c, and return a result
        return a + b + c
      })
      
      // Call the selector function and get a result
      const abc = selectABC(state)
      
      // could also be written as separate arguments, and works exactly the same
      const selectABC2 = createSelector(selectA, selectB, selectC, (a, b, c) => {
        // do something with a, b, and c, and return a result
        return a + b + c
      })
      
    16. Memoization is a form of caching. It involves tracking inputs to a function, and storing the inputs and the results for later reference. If a function is called with the same inputs as before, the function can skip doing the actual work, and return the same result it generated the last time it received those input values. This optimizes performance by only doing work if inputs have changed, and consistently returning the same result references if the inputs are the same.

    17. As an example, this component is written badly, because its useSelector call always returns a new array reference. That means the component will re-render after every dispatched action, even if the input state.todos slice hasn't changed:
      function TodoList() {
        // ❌ WARNING: this _always_ returns a new reference, so it will _always_ re-render!
        const completedTodos = useSelector(state =>
          state.todos.map(todo => todo.completed)
        )
      }
      
    18. Optimizing Selectors with Memoization​

      • Selectors used with useSelector or mapState will be re-run after every dispatched action, regardless of what section of the Redux root state was actually updated. Re-running expensive calculations when the input state sections didn't change is a waste of CPU time, and it's very likely that the inputs won't have changed most of the time anyway.
      • useSelector and mapState rely on === reference equality checks of the return values to determine if the component needs to re-render. If a selector always returns new references, it will force the component to re-render even if the derived data is effectively the same as last time. This is especially common with array operations like map() and filter(), which return new array references.
    1. For example, passing this selector to useSelector will cause the component to always re-render, because array.map() always returns a new array reference:
      // Bad: always returning a new reference
      const selectTodoDescriptions = state => {
        // This creates a new array reference!
        return state.todos.map(todo => todo.text)
      }
      
    2. However, there's a very important thing to remember here:

      useSelector compares its results using strict === reference comparisons, so the component will re-render any time the selector result is a new reference! This means that if you create a new reference in your selector and return it, your component could re-render every time an action has been dispatched, even if the data really isn't different.

    3. Fortunately, useSelector automatically subscribes to the Redux store for us! That way, any time an action is dispatched, it will call its selector function again right away. If the value returned by the selector changes from the last time it ran, useSelector will force our component to re-render with the new data. All we have to do is call useSelector() once in our component, and it does the rest of the work for us.
  5. Nov 2021
  6. Oct 2021
  7. Sep 2021
    1. Redux was created by Dan Abramov for a talk. It is a “state container” inspired by the unidirectional Flux data flow and functional Elm architecture. It provides a predictable approach to managing state that benefits from immutability, keeps business logic contained, acts as the single source of truth, and has a very small API.
  8. Aug 2021
    1. connect is pure connect automatically makes connected components “pure,” meaning they will only re-render when their props change – a.k.a. when their slice of the Redux state changes. This prevents needless re-renders and keeps your app running fast.
    1. The Redux FAQ has some rules of thumb to help decide whether state should go in Redux, or stay in a component.In addition, if you separate your state by domain (by having multiple domain-specific contexts), then the problem is less pronounced as well.
    1. I consistently see developers putting all of their state into redux. Not just global application state, but local state as well. This leads to a lot of problems, not the least of which is that when you're maintaining any state interaction, it involves interacting with reducers, action creators/types, and dispatch calls, which ultimately results in having to open many files and trace through the code in your head to figure out what's happening and what impact it has on the rest of the codebase.
  9. Oct 2020
    1. A first step would be to dispatch actions and update state via a reducer, something along the lines of useReducer in React.
  10. Jul 2020
    1. Sure you can hook up different reducers to manage different parts of your application, but the indirection of going through all these action creators and reducers is not optimal.

      On indirection through multiple action creators and reducers for managing different parts of an application

    2. consistently see developers putting all of their state into redux. Not just global application state, but local state as well. This leads to a lot of problems

      global vs local state management. Solutions are not one size fits all.

    3. One of the reasons redux was so successful was the fact that react-redux solved the prop drilling problem. The fact that you could share data across different parts of your tree by simply passing your component into some magical connect function was wonderful.

      Reasons why redux was so successful

  11. May 2020
  12. redux.js.org redux.js.org
    1. functional programming utility, and is included in Redux as a convenience. You might want to use it to apply several store enhancers in a row.

      store enhancers for redux; All compose does is let you write deeply nested function transformations without the rightward drift of the code. Don't give it too much credit!

  13. Apr 2020
    1. SPAs are incredibly common, popularised by client-side web frameworks like Angular, React and Vue.js.

      SPAs:

      • popularised by client-side web frameworks like Angular, React and Vue.js
      • real difference between the MVP app is shifting most of its work on the client side
      • there's client side MVC, MVVM (model-view-view-model) and FRP (functional reactive programming)

      Angular - client side MVC framework following its pattern, except it's running inside the users web browser.

      React - implementation of FRP. A little more flexible, but more concerned with state change events in data (often using some event store like Redux)

    1. In addition to these development tool middleware, it also adds redux-thunk by default, since thunks are the basic recommended side effects middleware for Redux.

      redux-thunk 默认加入

    1. I'm a Redux maintainer and creator of Redux Toolkit. FWIW, nothing about making async calls with Redux changes with Redux Toolkit. You'd still use an async middleware (typically redux-thunk), fetch data, and dispatch actions with the results.

      Redux Toolkit作者说了,RTK并没有改变任何异步操作的调用。还是可以使用中间件(象redux-thunk)获取数据和分发结果动作。

  14. redux.js.org redux.js.org
    1. reducer: any function with the signature (state, action) -> newState (ie, any function that could be used as an argument to Array.prototype.reduce)root reducer: the reducer function that is actually passed as the first argument to createStore. This is the only part of the reducer logic that must have the (state, action) -> newState signature.slice reducer: a reducer that is being used to handle updates to one specific slice of the state tree, usually done by passing it to combineReducerscase function: a function that is being used to handle the update logic for a specific action. This may actually be a reducer function, or it may require other parameters to do its work properly.higher-order reducer: a function that takes a reducer function as an argument, and/or returns a new reducer function as a result (such as combineReducers, or redux-undo)

      不同类型的函数。

    2. irst and foremost, combineReducers is simply a utility function to simplify the most common use case when writing Redux reducers.

      comineReucers只是简化通用案例的应用函数。

    3. functions should be relatively short and ideally only do one specific thing

      函数最好是单一职责

    1. 应用风格指南,值得参考的部分

    2. Redux itself does not care about how your application's folders and files are structured. However, co-locating logic for a given feature in one place typically makes it easier to maintain that code.Because of this, we recommend that most applications should structure files using a "feature folder" approach (all files for a feature in the same folder) or the "ducks" pattern (all Redux logic for a feature in a single file), rather than splitting logic across separate folders by "type" of code (reducers, actions, etc).

      强烈建议,应用将文件夹结构按照功能特性划分

    1. When a React component dispatches actions, how can another service catch the actions, accumulate and group them, then redispatch a single action?

      Catching actions to accumulate and group them

  15. Mar 2020
    1. you can make the component a container component by wrapping it in a higher-order component returned by connect().

      从示例看出来,connect可以连接函数式组件,不仅仅是类组件。

    2. react-redux包很简单,暴露了两个东西:Provider和connnect

    1. wrapping your entire application in a <Provider> component

      还是需要Provider哦

    2. React Redux now offers a set of hook APIs as an alternative to the existing connect() Higher Order Component. These APIs allow you to subscribe to the Redux store and dispatch actions, without having to wrap your components in connect()

      React Redux提供的一些hook api可以用来替代connect高阶组件。用来订阅Redux Store和分发动作,而无需connect()

    1. Notice that none of these changed anything about how Redux works. We're still creating a Redux store, dispatching action objects that describe "what happened", and returning updated state using a reducer function. Also, notice that the Redux Toolkit functions can be used no matter what approach was used to build the UI, since they just handle the "plain Redux store" part of the code. Our example used the store with a "vanilla JS" UI, but we could use this same store with React, Angular, Vue, or any other UI layer.

      Redux Toolkit并没有侵入Redux。可以用在React,Angular,Vue和其它UI层。所以,我想,应该也可以用在Taro上面!

    1. And no longer requires you to install the redux-thunk or redux-devtools-extension dependencies.

      Redux-Toolkit不再需要安装redux-thunk和redux-devtools-extension。

    1. The Redux Toolkit package is intended to be the standard way to write Redux logic. It was originally created to help address three common concerns about Redux:"Configuring a Redux store is too complicated""I have to add a lot of packages to get Redux to do anything useful""Redux requires too much boilerplate code"

      Redux Toolkit的用意是以标准方式编写Redux逻辑,帮助解决下面三个问题:

      • 配置store太复杂
      • 需要添加太多的包
      • 需要很多模板代码
    1. To do that, the react-redux library comes with 2 things: a component called Provider, and a function called connect.

      react-redux带来两样东西: 一个叫Provider的组件 一个叫connect的函数

    2. Reducer Rule #2: Reducers must be pure functions.

      Reducer规则2:必须是纯函数,不能用副作用。

    1. automatically handle all other action types by returning the current state

      默认为返回当前状态。

    2. in most apps, you won't need to use them yourself - createSlice is all you need.

      所以说,有时候使用createSlice就同时有了createReducer和createActions.

    1. The whole state of your app is stored in an object tree inside a single store. The only way to change the state tree is to emit an action, an object describing what happened. To specify how the actions transform the state tree, you write pure reducers.

      整个App状态存在单个Store对象树。改变状态的只有action,一个描述发生个么的对象。然后reducer负责状态改变。

  16. Feb 2020
  17. Jan 2020
    1. Instead of allowing any and all components to fetch and manipulate data, which can make debugging pretty much suck, we want to implement a pattern that's in line with the Single Responsibility Principle, and that keeps our code DRY.
  18. Nov 2019
    1. The most common signature for HOCs looks like this: // React Redux's `connect` const ConnectedComment = connect(commentSelector, commentActions)(CommentList); What?! If you break it apart, it’s easier to see what’s going on. // connect is a function that returns another function const enhance = connect(commentListSelector, commentListActions); // The returned function is a HOC, which returns a component that is connected // to the Redux store const ConnectedComment = enhance(CommentList); In other words, connect is a higher-order function that returns a higher-order component!

      probably a common and useful example that is used with Redux

  19. Aug 2019
    1. I was so fed up of the mega amounts of boilerplate with Redux and the recommendation of keeping your data loading at view level. It seems to me that things like this, with components being responsible for their own data, is the way to go in the future.
  20. Apr 2019
  21. Jan 2019
    1. 这样可以方便的进行单元测试,不需要模拟Api.fetch函数的具体返回结果。

      非模拟时,由redux-saga内部的调度器帮我们执行 next(在异步函数或副作用函数执行获得结果后);

      模拟时,由测试函数自己根据需要执行 next,所以说无需模拟 Api.fetch 函数的具体返回结果,从而为测试带来了方便

  22. Nov 2018
  23. Oct 2018
  24. Jul 2018
    1. const shopItemsSelector = state => state.shop.items const taxPercentSelector = state => state.shop.taxPercent

      This is a selector

    2. const subtotalSelector = createSelector( shopItemsSelector, items => items.reduce((acc, item) => acc + item.value, 0) )

      This is a memoized selector. items.reduce will be evaluated during the first call. Any subsequent call with the same parameters will return the result without computing items.reduce.

  25. Apr 2017
  26. Feb 2017
  27. Apr 2016
    1. I’ve personally seen two big benefits after adopting this strict separation in production apps. The first is that teams can more easily reuse the Presentation Components because they are nothing more than html/css. Over time this will allow the team to amass a great component library that is more composable allowing even greater code sharing across the organization. The second benefit is that teams can more easily upgrade the infrastructure that powers data flow throughout the application. Because this code is isolated in the Container Components we don’t need to untangle the plumbing code from the html/css that displays it. So you want to swap your global store for an ember service? No problem! We just modify the entry point in the Container Components and the data still flows down to any Presentational Components like it did before the refactor.
  28. Mar 2016
    1. Therefore we've built https://github.com/salsita/redux-side-effects which is at least "somehow" trying to simulate the Continuations with Effect handlers approach. The idea is very simple, if reducer was generator then you could simply yield side effects and return mutated app state. So the only condition is to turn all your reducers into function* and use yield* whenever you want to compose them.

      This also looks like an interesting and potentially relatively simple take on supporting effects in reducers.

    2. Very interesting thread with a discussion on how to improve handling of effects in Redux and make Redux applications composable ala. the Elm Architecture.

      Redux Loop looks like a very interesting take on supporting the Elm arch with the current version of Redux

    1. This is a useful introduction through recent developments in client-side data management and an overview of the different choices available when building a new application and guidance on what is appropriate for different sizes of app.

  29. Feb 2016
  30. Jan 2016
    1. All kinds of frameworks and architectures have state. In Ember apps and Backbone apps, state is in Models. In Angular apps, state is often in Factories and Services. In most Flux implementations, it is in Stores. How does Redux differ from these?

      Useful explanation of how state storage in Redux differs from other frameworks.

    2. Useful tutorial that covers combined usage on the client and server.

    1. You can call store.dispatch(action) from anywhere in your app, including components and XHR callbacks, or even at scheduled intervals.

      For an example of dispatch at regular intervals, the SoundRedux media player app dispatches an action every time the play time of the current song, measured in seconds, changes.

    2. Now todoApp just gives it the slice of the state to manage, and todos knows how to update just that slice.

      This is great for the simple case where there is no interdependent state. For ideas on what to do when one part of the state tree depends on another, see http://stackoverflow.com/questions/34782948/how-can-i-model-interdependent-data-with-cascading-updates-in-redux/34796163#34796163

  31. Dec 2015
  32. Sep 2015
    1. If your head boiled from reading the above section, imagine what it was like to write it.

      I find this slightly disturbing to read in the documentation (!)

  33. Aug 2015
    1. Probably the best explanation I've seen yet on the why and how of a functional approach to UI state management with Redux