React Native中按钮 onPress 触发Redux Action的实现咨询
Hey there! Let's break down exactly how to make your login button trigger a Redux action, handle the API call with Redux Saga, and react once you get that auth_token. I'll walk you through each step with code examples you can adapt to your project.
First, we need to set up the action types and creators that will kick off the login flow, plus a reducer to manage your auth state (loading status, token, and errors).
Action Types & Creators
// actions/authActions.js export const FETCH_DATA = 'FETCH_DATA'; export const FETCH_DATA_SUCCESS = 'FETCH_DATA_SUCCESS'; export const FETCH_DATA_FAILURE = 'FETCH_DATA_FAILURE'; // Trigger login API request export const fetchData = (username, password) => ({ type: FETCH_DATA, payload: { username, password } }); // Dispatch on successful authentication export const fetchDataSuccess = (authToken) => ({ type: FETCH_DATA_SUCCESS, payload: authToken }); // Dispatch on API failure export const fetchDataFailure = (error) => ({ type: FETCH_DATA_FAILURE, payload: error });
Auth Reducer
// reducers/authReducer.js import { FETCH_DATA, FETCH_DATA_SUCCESS, FETCH_DATA_FAILURE } from '../actions/authActions'; const initialState = { loading: false, authToken: null, error: null }; const authReducer = (state = initialState, action) => { switch (action.type) { case FETCH_DATA: return { ...state, loading: true, error: null }; case FETCH_DATA_SUCCESS: return { ...state, loading: false, authToken: action.payload, error: null }; case FETCH_DATA_FAILURE: return { ...state, loading: false, error: action.payload }; default: return state; } }; export default authReducer;
Redux Saga will handle the asynchronous API request. We'll listen for the FETCH_DATA action, call your login API, and dispatch success/failure actions based on the response.
// sagas/authSagas.js import { call, put, takeLatest } from 'redux-saga/effects'; import { FETCH_DATA, fetchDataSuccess, fetchDataFailure } from '../actions/authActions'; import AsyncStorage from '@react-native-async-storage/async-storage'; // Replace this with your actual login API endpoint async function authenticateUser(username, password) { const response = await fetch('https://your-api-domain.com/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); const data = await response.json(); if (!response.ok || !data.auth_token) { throw new Error(data.message || 'Authentication failed'); } return data.auth_token; } function* fetchDataSaga(action) { try { const { username, password } = action.payload; const authToken = yield call(authenticateUser, username, password); // Dispatch success action to update Redux state yield put(fetchDataSuccess(authToken)); // Optional: Store token locally right here in the saga yield call(AsyncStorage.setItem, 'auth_token', authToken); } catch (error) { // Dispatch failure action with error message yield put(fetchDataFailure(error.message)); } } // Listen for the latest FETCH_DATA action (prevents duplicate requests) export function* watchAuthSagas() { yield takeLatest(FETCH_DATA, fetchDataSaga); }
Now wire up your login screen to Redux so it can dispatch the fetchData action and react when the authToken is available.
// components/LoginScreen.js import React, { useState, useEffect } from 'react'; import { View, TextInput, Button, Text, ActivityIndicator, StyleSheet } from 'react-native'; import { connect } from 'react-redux'; import { fetchData } from '../actions/authActions'; import { useNavigation } from '@react-navigation/native'; // If using React Navigation const LoginScreen = ({ fetchData, loading, authToken, error }) => { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const navigation = useNavigation(); // Trigger follow-up actions when authToken is received useEffect(() => { if (authToken) { // Example: Navigate to home screen navigation.navigate('Home'); // Optional: Store token locally here instead of in the saga // AsyncStorage.setItem('auth_token', authToken); } }, [authToken, navigation]); const handleLoginPress = () => { if (!username.trim() || !password.trim()) { alert('Please enter both username and password'); return; } fetchData(username, password); }; return ( <View style={styles.container}> <TextInput style={styles.input} placeholder="Username" value={username} onChangeText={setUsername} autoCapitalize="none" /> <TextInput style={styles.input} placeholder="Password" value={password} onChangeText={setPassword} secureTextEntry autoCapitalize="none" /> {loading ? ( <ActivityIndicator size="large" color="#007AFF" /> ) : ( <Button title="Login" onPress={handleLoginPress} /> )} {error && <Text style={styles.errorText}>{error}</Text>} </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', paddingHorizontal: 20, }, input: { borderBottomWidth: 1, borderBottomColor: '#ccc', marginBottom: 15, paddingVertical: 8, }, errorText: { color: 'red', marginTop: 10, textAlign: 'center', }, }); // Map Redux state to component props const mapStateToProps = (state) => ({ loading: state.auth.loading, authToken: state.auth.authToken, error: state.auth.error, }); // Map action creators to component props const mapDispatchToProps = { fetchData }; export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen);
Don't forget to set up your Redux store to use the saga middleware so your sagas can run.
// store.js import { createStore, combineReducers, applyMiddleware } from 'redux'; import createSagaMiddleware from 'redux-saga'; import authReducer from './reducers/authReducer'; import { watchAuthSagas } from './sagas/authSagas'; // Combine all reducers (add more if you have others) const rootReducer = combineReducers({ auth: authReducer, }); // Initialize saga middleware const sagaMiddleware = createSagaMiddleware(); // Create store with middleware const store = createStore(rootReducer, applyMiddleware(sagaMiddleware)); // Run your root saga sagaMiddleware.run(watchAuthSagas); export default store;
- Token Storage: You can store the
auth_tokeninAsyncStorageeither in the saga (right after a successful login) or in the component'suseEffect—pick whichever fits your workflow better. - Prevent Duplicate Requests: We used
takeLatestin the saga, which cancels any pending requests if a new one is triggered (perfect for stopping double-clicks on the login button). If you need to allow concurrent requests, usetakeEveryinstead. - Error Handling: The example shows an error message in the component, but you could also use a toast notification or custom alert for a better user experience.
- Navigation: If you're not using React Navigation, replace the navigation logic with whatever routing your app uses (e.g., switching screens manually).
内容的提问来源于stack exchange,提问作者Surinder Bhomra

