• 使用Redux处理异步


    useSelector,useDispatch

    首先,也是最重要的一点。如果你不知道要不要用redux,那么最好不要用。

    redux主要解决的问题是统一来源的状态管理。

    1. 全局state存放在store里。store.getState()可以获得state树。
    2. 给store,发送action可以通过reducer生成新的state。store.dispatch({type:SOME_ACTION, data: {})
    3. 订阅store,或者新的state。store.subscribe(() => store.getState())

    React-redux主要解决的是第二条。使用connect方法把state和actions都作为props注入到了组件里。

    比如现在有一个Counter组件。increment做为action creator。状态就是value++。那么可以写成:

    function Counter(props) {
      // ...
      return (<Button onClick={() => props.increment()}>+</Button>)
    }
    
    const mapStateToProps = (state) => ({
      value: state.value,
    });
    const mapActionToProps = (dispatch) => ({
      increment: dispatch(increment()),
    })
    connect(mapStateToProps, mapActionToProps)(Counter);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    大概就是上面这样的。使用了react-redux的hooks呢,画风一转。整个显得清晰了很多。当然这里也不能少了redux toolkit的帮助。

    要实现redux应用的完全体就少不了要创建redux的store等这些前期的配置工作。为了减少这些繁琐的配置,redux开发了toolkit这一套工具。项目代码中都有,可以参考。这里不多介绍。

    使用了react-redux之后看起来就是这样了。我们在前期没有多做action(action creator)和reducer的介绍。突然出现不能体现它的简化后的好处。有一点,action和reducer通常都放在不同的地方。使用起来不是十分方便。

    在新的实现中这些都在slice文件中,方便统一的管理。维护起来也好很多。

    import { createSlice } from '@reduxjs/toolkit';
    
    export const counterSlice = createSlice({
      name: 'counter',
      initialState: {
        value: 0,
      },
      reducers: {
        increment: state => {
          state.value += 1;
        },
        decrement: state => {
          state.value -= 1;
        },
        incrementByAmount: (state, action) => {
          state.value += action.payload;
        },
      },
    });
    
    // Action creators are generated for each case reducer function
    export const { increment, decrement, incrementByAmount } = counterSlice.actions;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    这是使用了redux toolkit的好处。action的部分基本不用处理。它会自动生成出来action creator。

    react-redux的connect方法也省去了。代替的方案是useSelector和useDispatch两个方法。在ReduxCounter组件里:

    // ...
    
    const ReduxCounter: React.FC<ReduxCounterProps> = props => {
      // ...
      const count = useSelector((state: RootState) => { // 1
        return state.counter.value;
      });
      const dispatch = useDispatch();    // 2
      const onAdd = () => dispatch(increment()); //3
    
      return (
              <View>
                <Text>{count}</Text>
              </View>
            <View style={styles.buttonWrapper}>
              <TouchableHighlight style={styles.button} onPress={onAdd}>
                <View style={styles.buttonInsider}>
                  <Text>+</Text>
                </View>
              </TouchableHighlight>
      );
    };
    
    export { ReduxCounter };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    // 1. 使用useSelector获取state树对应到这个组件的状态
    // 2. 使用useDispatch获取dispatch方法,可以发送action
    // 3. 在处理add点击的时候调用dispatch方法发送increment() action creator生成的action。

    处理异步

    在一般的开发中需要处理很多的网络请求,缓存读写等情况。这些都是异步的。Redux提供了两种方法来处理异步的情况:

    1. createAsyncThunk
    2. RTK Query, redux toolkit query

    下面主要通过异步缓存读写来演示如何使用这两种方法。

    在React Native里用来处理简单的字符串存取的库一般是@react-native-async-storage/async-storage

    import:

    import AsyncStorage from '@react-native-async-storage/async-storage';
    
    • 1

    读、写,删除:

    // inside a async function
    
    const ret = await AsyncStorage.getItem('@your-key');
    await AsyncStorage.setItem('@your-key', xxx-xxx-xxx);
    await AsyncStorage.removeItem('@your-key');
    
    • 1
    • 2
    • 3
    • 4
    • 5

    createAsyncThunk

    很久以前,有一个用redux就用过的redux插件叫做redux-thunk。现在我们有了createAsyncThunk

    在使用的时候和前文提到的slice的套路基本一致。首先我们来新建一个slice叫做authSlice。在里面填空比如:name,initialState。reducers为空对象,处理非异步的情况。

    新增的是:

    1. 新建相关的thunk
    2. extraReducers

    新建thunk

    const authenticate = createAsyncThunk<boolean, string, any>(
      'users/auth',
      async (token: string) => {
        try {
          await AsyncStorage.setItem(GITHUB_TOKEN_KEY, token);
          return true;
        } catch (e) {
          console.error('ERROR:', e);
          return false;
        }
      },
    );
    
    const authSlice = createSlice({
      name: 'auth',
      initialState: {
        authed: false,
      },
      reducers: {},
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    添加extraReducers

    异步的thunks就放在extraReducers里:

    const authSlice = createSlice({
      name: 'auth',
      initialState: {
        authed: false,
      },
      reducers: {},
      extraReducers: builder => {
        builder.addCase(authenticate.fulfilled, state => {
          state.authed = true;
        });
    
        builder.addCase(removeToken.rejected, state => {
          state.authed = false;
        });
      },
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    最后export出去:

    export { authenticate, removeToken, authSlice };
    
    • 1

    后续还要在store里绑定reducer。

    export const store = configureStore({
      reducer: { counter: counterSlice.reducer, auth: authSlice.reducer },
    });
    
    • 1
    • 2
    • 3

    使用上和之前的countSlice一样:

    // 读取state值
    const authed = useSelector((state: RootState) => state.auth.authed);
    
    • 1
    • 2

    TokenScreen.tsx文件里可以看到具体代码。发出thunk:

      const dispatch = useAppDispatch(); // 1
      try {
        const ret = await dispatch(authenticate(text)).unwrap(); // 2
        if (ret) {
          console.log('>result action: ', 'DONE');
        }
    
        navigation.replace('Tabs');
      } catch (e) {
        // TODO: deal with the error 
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 拿到useAppDispatch方法。在src/store.ts文件里。本质就是useDispatch
    2. 处理分发的thunk的结果的时候可以用redux提供的unwrap方法直接得到结果。在catch语句里处理错误。
  • 相关阅读:
    Godot 4.0 文件系统特性的总结
    Al中秋节由来
    R语言kaggle 游戏数据探索与可视化
    java健身房信息管理系统计算机毕业设计MyBatis+系统+LW文档+源码+调试部署
    复习十二:广义表
    最新版两款不同版SEO超级外链工具PHP源码
    文件对比工具Beyond Compare 4(4.4.7) for Mac
    JavaScript:File API和Blob API
    【Redis学习笔记】第一章 Redis入门与安装
    传统纸业如何实现数字化,S2B2C系统网站赋能渠道提升供应链管理效率
  • 原文地址:https://blog.csdn.net/song854601134/article/details/126267214