- Redux is typically used with the React-Redux library for integrating Redux and React together
- Redux Toolkit is the recommended way to write Redux logic
- State describes the condition of the app a a point in time, and UI renders based on that state
- When something happens in the app:
- The UI dispatches an action
- The store runs the reducers, and the state is updated based on what occurred
- The store notifies the UI that the state has changed
- The UI re-renders based on new state
- Actions are plain objects with a type field that describe "what happened" in the app
- Reducers are functions that calculate a new state value base on previous state + an action
- A Redux store runs the root reducer whenever an action is dispatched
configureStore
acceptsreducer
function as a named argumentconfigureStore
automatically sets up the store with good default settings
- A "slice" contains the reducer logic and actions related to a specific feature/section of the Redux state
- Redux Toolkit's
createSlice
API generates action creators and action types for each individual reducer function you provide
- Should only calculate a new state value on the state and action arguments
- Must make immutable updates by copying the existing state
- Cannot contain any asynchronous logic or other side effects
- Redus Toolkit's
createSlice
API uses Immer to allow immutable updates
- Thunks receive
dispatch
andgetState
as arguments - Redux Toolkit enables the
redux-thunk
middleware by default
- 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 state in React components
- Reducers always calculate a new state immutably, by copying existing state values and modifying the copies with the new data
- The Redux Toolkit
createSlice
function generates slice reducer functions for you, and lets you write mutating code that is turned into safe immutable updates - Those slice reducer functions are added to teh
reducer
field inconfigureStore
, and that defines the data and state field names inside Redux store
- 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
createSlice
will generate action creator functions for each reducer we add to a slice- Call
dispatch(somActionCreator())
in a component to dispatch an action - Reducers will run, check to see if this action is relevant, and return new state 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.
- Any component can read any data that is in the Redux store
- Multiple components can read the same data, even at the same time
- 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. They can read multiple peices of data from the store, and reshape the data as needed for display.
- Any component can dispatch actions to cause state updates
createSlice
andcreateAction
cn accept a prepare callbak that returns the action payload- Unique IDs and other rando values should put in the action, not calculated in the reducer
- Reducers can contain whateve logic is needed to calculate the next state
- Action objects should contain just enough info to describe what happened
- Selectores are functions that get the Redux
state
as an argument, and return some data
- The standard async middleware is called
redux-thunk
, which is included in Redux Toolkit - Thunk functions recieve
dispatch
andgetState
as arguments, and can use those as part of async logic
- The typical pattern is dispatching a "pending" action before the call, then either a "success" containing data or a "failure" action containing error
- Loading state should usually be stored as an enum, like
idle | loading | succeeded | failed
createAsyncThunk
accepts a payload creator callback that should return a Promise and generatespending/fullfilled/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
usingextraReducers
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. - Thunks can return promises. For
createAsyncThunk
specifically, you canawait dispatch(someThunkCreatorAction()).unwrap()
to handle the request success or failure at the component level.
- Redux Toolkit re-exports the createSelector function from Reselect, which generates memoized selectors
- Memoized selectors will only recalculate the results if the input selectors return new values
- Memoization can skip expensive calculations, and ensure the same result references are returned
- Avoid creating new objec/array references inside of
useSelector
- those will cause unnecessary re-renders - Memoized selector functions can be passed to
useSelector
to optimize rendering useSelector
can accept an alternate comparison function likeshallowEqual
instead of reference equality- Components can be wrapped in
React.memo
to only re-render if their props change - List rendering can be optimized by having list parent components read just an array of items IDs, passing the IDs to list item children, and retrieving items by ID in the children
- Normalization means no duplication of data and keeping items stored in a lookup table by item ID
- Normalized state shape usually looks like
{ids:[], entities:{}}
- Item IDs can be kept in sorted order by passing in a
sortComparer
option - The adapter object includes:
adapter.getInitialState
, which can accept additional state fields liek loading state- Prebuilt reducers for common cases like
setAll, addMany, upsertMany, upsertOne, removeMany
adapter.getSelectore
, which generate selectors likeselectAll, selectById