【redux】@reduxjs/tookit快速上手

前言

  • 最近兴起使用redux tookit,赶紧学一下。

@reduxjs/tookit

  • 主要是为了解决redux样板代码太多。
  • 学习此篇请熟练使用redux,否则可能看不懂。

基础使用

  • 首先,每个reducer有自己的作用域namespace,然后由combinedReducer集合成完整的reducer 。每个小reducer叫slice。
  • 使用create-react-app创建项目
  • 首先需要制作store,删除app.tsx中无用代码。

configureStore

  • 创建store由原来createStore变更为configureStore。
  • 中间件啥的以配置形式传入。
  • 其内置了immer与thunk,所以可以以点的形式进行修改,并且可以派发函数。
export interface ConfigureStoreOptions<S = any, A extends Action = AnyAction, M extends Middlewares<S> = Middlewares<S>> {
    /**
     * A single reducer function that will be used as the root reducer, or an
     * object of slice reducers that will be passed to `combineReducers()`.
     */
    reducer: Reducer<S, A> | ReducersMapObject<S, A>;
    /**
     * An array of Redux middleware to install. If not supplied, defaults to
     * the set of middleware returned by `getDefaultMiddleware()`.
     */
    middleware?: ((getDefaultMiddleware: CurriedGetDefaultMiddleware<S>) => M) | M;
    /**
     * Whether to enable Redux DevTools integration. Defaults to `true`.
     *
     * Additional configuration can be done by passing Redux DevTools options
     */
    devTools?: boolean | DevToolsOptions;
    /**
     * The initial state, same as Redux's createStore.
     * You may optionally specify it to hydrate the state
     * from the server in universal apps, or to restore a previously serialized
     * user session. If you use `combineReducers()` to produce the root reducer
     * function (either directly or indirectly by passing an object as `reducer`),
     * this must be an object with the same shape as the reducer map keys.
     */
    preloadedState?: PreloadedState<CombinedState<NoInfer<S>>>;
    /**
     * The store enhancers to apply. See Redux's `createStore()`.
     * All enhancers will be included before the DevTools Extension enhancer.
     * If you need to customize the order of enhancers, supply a callback
     * function that will receive the original array (ie, `[applyMiddleware]`),
     * and should return a new array (such as `[applyMiddleware, offline]`).
     * If you only need to add middleware, you can use the `middleware` parameter instead.
     */
    enhancers?: StoreEnhancer[] | ConfigureEnhancersCallback;
}
  • 创建store.ts导出store :
export const store = configureStore({
    reducer: {
      counter: counterReducer,
    },
});
  • counterReducer是由createSlice创建出来的,而createSlice通过你的传递,可以通过返回对象自动生成其reducer、actions。

createSlice

  • 先制作个基础例子,createSlice可以传入reducers的逻辑再生成reducer与action:
/*
 * @Author: yehuozhili
 * @Date: 2021-08-14 15:18:10
 * @LastEditors: yehuozhili
 * @LastEditTime: 2021-08-14 16:07:17
 * @FilePath: \learnrtk\src\store.ts
 */

import { configureStore, createSlice } from "@reduxjs/toolkit";

export interface CounterState {
    value: number;
    status: 'idle' | 'loading' | 'failed';
  }
  const initialState: CounterState = {
    value: 0,
    status: 'idle',
  };

export const counterSlice = createSlice({
    name: 'counter',
    initialState,
    reducers: {
      increment: (state) => {
        state.value += 1;
      },
      decrement: (state) => {
        state.value -= 1;
      },
      incrementByAmount: (state, action) => {
        state.value += action.payload;
      },
    },
 
});

//useSelector获取值
export const selectCount = (state: RootState) => state.counter.value;
//actions
export const { increment, decrement, incrementByAmount } = counterSlice.actions;

const counterReducer = counterSlice.reducer

export const store = configureStore({
    reducer: {
      counter: counterReducer,
    },
});

export type RootState = ReturnType<typeof store.getState>;
  • name则是namespace,生成的丢给configStore的reducer即可。
  • 然后在根路径下链接store,这个和以前是一样的:
  <Provider store={store}>
      <App />
  </Provider>
  • 然后我们使用counter渲染出来,再派发去修改状态:
import { useDispatch, useSelector } from 'react-redux';
import './App.css';
import { decrement, increment, selectCount } from './store';


function App() {
  const dispatch = useDispatch()
  const state = useSelector(selectCount)
  return (
    <div className="App">
      {
        state
      }
      <button onClick={()=>{
        dispatch(increment())
      }}>+</button>
       <button onClick={()=>{
        dispatch(decrement())
      }}>-</button>
    </div>
  );
}

export default App;
  • 此时加减都正常了。
  • 下面使用异步加减:

createAsyncThunk

  • createAsyncThunk是一个便于创建异步reducer处理逻辑的函数,它自动生成3中actionCreator ,pending,fullfilled,rejected :
export declare type AsyncThunk<Returned, ThunkArg, ThunkApiConfig extends AsyncThunkConfig> = AsyncThunkActionCreator<Returned, ThunkArg, ThunkApiConfig> & {
    pending: AsyncThunkPendingActionCreator<ThunkArg, ThunkApiConfig>;
    rejected: AsyncThunkRejectedActionCreator<ThunkArg, ThunkApiConfig>;
    fulfilled: AsyncThunkFulfilledActionCreator<Returned, ThunkArg, ThunkApiConfig>;
    typePrefix: string;
};
  • 而它返回值,本身就是个action:
  function fetchCount(amount = 1) {
    return new Promise<{ data: number }>((resolve) =>
      setTimeout(() => resolve({ data: amount }), 500)
    );
  }
  
  export const incrementAsync = createAsyncThunk(
    'counter/fetchCount',
    async (amount?: number) => {
      const response = await fetchCount(amount);
      return response.data;
    }
  );
  • 可以直接进行派发:
import { decrement, increment, incrementAsync, selectCount, selectCountStatus } from './store';


function App() {
  const dispatch = useDispatch()
  const state = useSelector(selectCount)
  const st = useSelector(selectCountStatus)
  return (
    <div className="App">
      {
       st === 'loading'?'loading':  state
      }
      <button onClick={()=>{
        dispatch(increment())
      }}>+</button>
       <button onClick={()=>{
        dispatch(decrement())
      }}>-</button>
       <button onClick={()=>{
        dispatch(incrementAsync())
      }}>+</button>
    </div>
  );
}

export default App;
  • 在createSlice时,可使用extraReducer进行处理:
export const counterSlice = createSlice({
    name: 'counter',
    initialState,
    reducers: {
      increment: (state) => {
        state.value += 1;
      },
      decrement: (state) => {
        state.value -= 1;
      },
      incrementByAmount: (state, action) => {
        state.value += action.payload;
      },
    },
    extraReducers:(builder)=>{
        builder.addCase(incrementAsync.pending,(state) => {
            state.status = 'loading';
          })
          builder.addCase(incrementAsync.fulfilled,(state,action) => {
            state.status = 'idle';
            state.value += action.payload;
          })
    }
});

  • builder除了addCase,还有addMatcher以及addDefaultCase,类似于链式调用的使用方式,可直接再后面继续点。
  • 这样就可以在异步的不同阶段修改状态。
  • 当然也可以手动编写异步函数

createReducer&createAction

  • 前面看了createSlice的强大能力,当然你也可以手动写action,使用createAction函数可以帮你自动生成对象,让你可以使用.type来取得所要的action,而不需要像以前引入常量了。
const actionCreator = createAction('SOME_ACTION_TYPE')

console.log(actionCreator.toString())
// "SOME_ACTION_TYPE"

console.log(actionCreator.type)
// "SOME_ACTION_TYPE"
  • 而reducer则可以使用builder进行点链接:
const reducer = createReducer({}, (builder) => {
  // actionCreator.toString() will automatically be called here
  // also, if you use TypeScript, the action type will be correctly inferred
  builder.addCase(actionCreator, (state, action) => {})

  // Or, you can reference the .type field:
  // if using TypeScript, the action type cannot be inferred that way
  builder.addCase(actionCreator.type, (state, action) => {})
})

creatApi

  • creatApi可以返回自定义hook,reducer,reducer前缀,中间件:
// Need to use the React-specific entry point to import createApi
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { Pokemon } from './types'

// Define a service using a base URL and expected endpoints
export const pokemonApi = createApi({
  reducerPath: 'pokemonApi',
  baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
  endpoints: (builder) => ({
    getPokemonByName: builder.query<Pokemon, string>({
      query: (name) => `pokemon/${name}`,
    }),
  }),
})

// Export hooks for usage in functional components, which are
// auto-generated based on the defined endpoints
export const { useGetPokemonByNameQuery } = pokemonApi
import { configureStore } from '@reduxjs/toolkit'
// Or from '@reduxjs/toolkit/query/react'
import { setupListeners } from '@reduxjs/toolkit/query'
import { pokemonApi } from './services/pokemon'

export const store = configureStore({
  reducer: {
    // Add the generated reducer as a specific top-level slice
    [pokemonApi.reducerPath]: pokemonApi.reducer,
  },
  // Adding the api middleware enables caching, invalidation, polling,
  // and other useful features of `rtk-query`.
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(pokemonApi.middleware),
})

// optional, but required for refetchOnFocus/refetchOnReconnect behaviors
// see `setupListeners` docs - takes an optional callback as the 2nd arg for customization
setupListeners(store.dispatch)
  • 在组件中使用类似于umi的useRequest:
import * as React from 'react'
import { useGetPokemonByNameQuery } from './services/pokemon'

export default function App() {
  // Using a query hook automatically fetches data and returns query values
  const { data, error, isLoading } = useGetPokemonByNameQuery('bulbasaur')
  // Individual hooks are also accessible under the generated endpoints:
  // const { data, error, isLoading } = pokemonApi.endpoints.getPokemonByName.useQuery('bulbasaur')

  return (
    <div className="App">
      {error ? (
        <>Oh no, there was an error</>
      ) : isLoading ? (
        <>Loading...</>
      ) : data ? (
        <>
          <h3>{data.species.name}</h3>
          <img src={data.sprites.front_shiny} alt={data.species.name} />
        </>
      ) : null}
    </div>
  )
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

业火之理

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值