• [React] Redux Toolkit notes


    Highlights:

    • Solve the complexities of write Redux (actions, reducers, selector...middleware...)
    • Solve the immutable pattern with nested spread operator by using Immer
    • Wrap Redux-thunk for async action
    • Using Entity pattern

    createSlice

    createSlice returns a "slice" object that contains the generated reducer function as a field named reducer, and the generated action creators inside an object called actions.

    import { createSlice } from '@reduxjs/toolkit';
    
    export const counterSlice = createSlice({
      name: 'counter',  // also prefix for actions
      initialState: {
        value: 0,
      },
      reducers: {
        increment: state => {
          // Redux Toolkit allows us to write "mutating" logic in reducers. It
          // doesn't actually mutate the state because it uses the Immer library,
          // which detects changes to a "draft state" and produces a brand new
          // immutable state based off those changes
          state.value += 1;
        },
        decrement: state => {
          state.value -= 1;
        },
        incrementByAmount: (state, action) => {
          state.value += action.payload;
        },
      },
    });
    
    export const { increment, decrement, incrementByAmount } = counterSlice.actions;
    
    // The function below is called a thunk and allows us to perform async logic. It
    // can be dispatched like a regular action: `dispatch(incrementAsync(10))`. This
    // will call the thunk with the `dispatch` function as the first argument. Async
    // code can then be executed and other actions can be dispatched
    export const incrementAsync = amount => dispatch => {
      setTimeout(() => {
        dispatch(incrementByAmount(amount));
      }, 1000);
    };
    
    // The function below is called a selector and allows us to select a value from
    // the state. Selectors can also be defined inline where they're used instead of
    // in the slice file. For example: `useSelector((state) => state.counter.value)`
    export const selectCount = state => state.counter.value;
    
    export default counterSlice.reducer;

    createAsyncThunk

    Mainly, you don't want to write "GET_BOOKS", "GET_BOOKS_FULFILLED", "GET_BOOKS_ERROR" actions.

    import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
    import { userAPI } from './userAPI'
    
    // First, create the thunk
    const fetchUserById = createAsyncThunk(
      'users/fetchByIdStatus',
      async (userId, thunkAPI) => {
        const response = await userAPI.fetchById(userId)
        return response.data
      }
    )
    
    // Then, handle actions in your reducers:
    const usersSlice = createSlice({
      name: 'users',
      initialState: { entities: [], loading: 'idle' },
      reducers: {
        // standard reducer logic, with auto-generated action types per reducer
      },
      extraReducers: {
        // Add reducers for additional action types here, and handle loading state as needed
        [fetchUserById.fulfilled]: (state, action) => {
          // Add user to the state array
          state.entities.push(action.payload)
        }
      }
    })
    
    // Later, dispatch the thunk as needed in the app
    dispatch(fetchUserById(123))

    createEntityAdapter

    Ideas / api come from @ngrx/entity. Export you APIs to deal with data CRUD. Just toolkit using Immer as internal API.

    import {
      createEntityAdapter,
      createSlice,
      configureStore,
    } from '@reduxjs/toolkit'
    
    type Book = { bookId: string; title: string }
    
    const booksAdapter = createEntityAdapter<Book>({
      // Assume IDs are stored in a field other than `book.id`
      selectId: (book) => book.bookId,
      // Keep the "all IDs" array sorted based on book titles
      sortComparer: (a, b) => a.title.localeCompare(b.title),
    })
    
    const booksSlice = createSlice({
      name: 'books',
      initialState: booksAdapter.getInitialState(),
      reducers: {
        // Can pass adapter functions directly as case reducers.  Because we're passing this
        // as a value, `createSlice` will auto-generate the `bookAdded` action type / creator
        bookAdded: booksAdapter.addOne,
        booksReceived(state, action) {
          // Or, call them as "mutating" helpers in a case reducer
          booksAdapter.setAll(state, action.payload.books)
        },
      },
    })
    
    const store = configureStore({
      reducer: {
        books: booksSlice.reducer,
      },
    })
    
    type RootState = ReturnType<typeof store.getState>
    
    console.log(store.getState().books)
    // { ids: [], entities: {} }
    
    // Can create a set of memoized selectors based on the location of this entity state
    const booksSelectors = booksAdapter.getSelectors<RootState>(
      (state) => state.books
    )
    
    // And then use the selectors to retrieve values
    const allBooks = booksSelectors.selectAll(store.getState())
  • 相关阅读:
    Unity 3D 一个简单的角色控制脚本
    Unity3D 纹理偏移(TextureOffset)浅析
    递归函数的原理
    彻底搞定 C/C++ 指针
    zygote的分裂
    SystemServer分析
    Zygote原理学习
    Vmware Linux虚拟机磁盘扩容方法
    Ubuntu12.04 64bit版本下载Android源码完整教程
    Android2.2源码属性服务分析
  • 原文地址:https://www.cnblogs.com/Answer1215/p/14223915.html
Copyright © 2020-2023  润新知