♻️

Redux

Overview

  • Redux uses a "one-way data flow" app structure
    • State describes the condition of the app at a point in time, and UI is rendered based on that state
    • When something happens in the app:
      1. The UI dispatches an action
      2. The store runs the reducers, and the state is updated based on what occurred
      3. The store notifies the UI that the state has changed
    • The UI re-renders based on the new state
  • Concepts
    • Actions are plain objects with a type field, and describe "what happened" in the app
    • Actions also contain a payload that can be accessed in reducers when calculating new state
    • Reducers are functions that calculate a new state value based on previous state + an action (state,action)=>{}
    • A Redux store runs the root reducer whenever an action is dispatched
  • Usage
    • Config
      • A redux store can be configured via the Redux Toolkit configureStore API
        • configureStore accepts a reducer function as a named argument
        • Wrapping the app with <Provider store={store}> enables all components to use the store
        • Global state should go in the Redux store, local state should stay in React components
    • Slices
      • Redux Toolkit's createSlice API generates action creators and action types for each individual reducer function you provide
        • A "slice" contains the reducer logic and actions related to a specific feature / section of the Redux state
    • Reducers
      • Should only calculate a new state value based on the state and action arguments
      • Must make immutable updates by copying the existing state
      • Cannot contain any asynchronous logic or other "side effects"
      • Redux Toolkit's createSlice API uses Immer to allow "mutating" immutable updates
    • Thunks
      • For asynchronous logic (AJAX calls)
      • Thunks receive dispatch and getState as arguments
      • Redux Toolkit enables the redux-thunk middleware by default
    • Component State / useSelector
      • React components read data from the store with the useSelector hook
      • Selector functions receive the whole state object, and should return a value
      • 💡
        Selectors will re-run whenever the Redux store is updated, and if the data they return has changed, the component will re-render
    • Dispatching actions / useDispatch
      • createSlice will generate action creator functions for each reducer we add to a slice
      • Call dispatch(someActionCreator()) in a component to dispatch an action
      • Reducers will run, check to see if this action is relevant, and return new state if appropriate
      • 💡
        Temporary data like form input values should be kept as React component state. Dispatch a Redux action to update the store when the user is done with the form.
    • Using Data
      • Components should extract the smallest amount of data they need to render themselves
      • Components can combine values from props, state, and the Redux store to determine what UI they need to render.
      • Any component can dispatch actions to cause state updates
      • Redux action creators can prepare action objects with the right contents
        1. createSlice and createAction can accept a "prepare callback" that returns the action payload
        2. Unique IDs and other random values should be put in the action, not calculated in the reducer
        3. 💡
          Reducers should contain the actual state update logic
          • Reducers can contain whatever logic is needed to calculate the next state
          • Action objects should contain just enough info to describe what happened
    • Async / More on Thunks
      • Redux requires plugins/middleware to enable async logic
        • The standard async middleware is called redux-thunk, which is included in Redux Toolkit
      • Redux Toolkit has a createAsyncThunk API that dispatches these actions for you
        • createAsyncThunk accepts two arguments:
          • A string that will be used as the prefix for the generated action types
          • A "payload creator" callback that should return a Promise, and generates pending/fulfilled/rejected action types automatically
        • Generated action creators like fetchPosts dispatch those actions based on the Promise you return
        • You can listen for these action types in createSlice using the extraReducers field, and update the state in reducers based on those actions.
        • Action creators can be used to automatically fill in the keys of the extraReducers object so the slice knows what actions to listen for.
References