After React hooks came out, it opened a door to front-end wheels. Various Hooks tools are flying all over the sky. react-redux has also kept up with this wave of trends.
The project code is here
useSelector,useDispatch
First of all, and the most important point. If you don't know whether to use redux, it is best not to use it.
The main problem redux solves is the state management of the unified source.
- The global state is stored in the store.
store.getState()
can get the state tree. - Send action to store to generate new state through reducer.
store.dispatch({type:SOME_ACTION, data: {})
. - Subscribe to store, or new state.
store.subscribe(() => store.getState())
.
React-redux mainly solves the second one. Use the connect
method to inject state and actions into the component as props.
For example, there is now a Counter
component. increment as an action creator. The state is value++. It can be written as:
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);
It's probably like the above. Using react-redux hooks, the style has changed. The whole is much clearer. Of course, the help of redux toolkit cannot be omitted here.
To achieve the complete body of the redux application, it is indispensable to create a redux store and other preliminary configuration work. In order to reduce these tedious configurations, redux has developed a toolkit. It is included in the project code, you can refer to it. Not much introduction here.
It looks like this after using react-redux. We didn't do much introduction of action (action creator) and reducer in the early stage. The sudden appearance does not reflect its simplified benefits. One thing is that action and reducer are usually placed in different places. It is not very convenient to use.
In the new implementation, these are all in the slice file, which is convenient for unified management. It's also much better to maintain.
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;
This is the benefit of using the redux toolkit. The action part is basically not processed. It will automatically generate an action creator.
The connect method of react-redux is also omitted. The alternative scheme is useSelector and useDispatch two methods. In the ReduxCounter component:
// ...
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. Use useSelector
get the state of the state tree corresponding to this component
// 2. Use useDispatch
get the dispatch method, you can send action
// 3. When processing the add click, call the dispatch method to send the action generated by the increment()
Handle asynchronous
In general development, many network requests, cache reads and writes need to be processed. These are all asynchronous. Redux provides two methods to handle asynchronous situations:
- createAsyncThunk
- RTK Query, redux toolkit query
The following mainly demonstrates how to use these two methods through asynchronous cache reading and writing.
The library used to handle simple string access in React Native is generally @react-native-async-storage/async-storage
.
import:
import AsyncStorage from '@react-native-async-storage/async-storage';
Read, write, delete:
// inside a async function
const ret = await AsyncStorage.getItem('@your-key');
await AsyncStorage.setItem('@your-key', xxx-xxx-xxx);
await AsyncStorage.removeItem('@your-key');
createAsyncThunk
A long time ago, there was a redux plugin called redux-thunk
that I used with redux. Now we have createAsyncThunk
.
When used, it is basically the same as the slice routine mentioned above. First, let's create a new slice called authSlice
. Fill in the blanks such as name, initialState. The reducers are empty objects to handle non-asynchronous situations.
The new ones are:
- Create a related thunk
extraReducers
New 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: {},
});
Add extraReducers
Asynchronous thunks are placed in 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;
});
},
});
Finally export out:
export { authenticate, removeToken, authSlice };
The reducer will be bound to the store later.
export const store = configureStore({
reducer: { counter: counterSlice.reducer, auth: authSlice.reducer },
});
The usage is the same as the previous countSlice:
// 读取state值
const authed = useSelector((state: RootState) => state.auth.authed);
The specific code can be seen in the TokenScreen.tsx Issue a 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
}
- Get the
useAppDispatch
method. In the src/store.ts file. The essence isuseDispatch
. - When processing the result of the distributed thunk, you can use the
unwrap
method provided by redux to get the result directly. Handle the error in the catch statement.
RTK Query
To be continued...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。