Javascript Redux reducer被调用两次
我正在使用Redux开发React本机应用程序 该应用程序具有注册屏幕和一个简单的表单:Javascript Redux reducer被调用两次,javascript,reactjs,react-native,react-redux,Javascript,Reactjs,React Native,React Redux,我正在使用Redux开发React本机应用程序 该应用程序具有注册屏幕和一个简单的表单: import React from 'react'; import { Alert, Button, KeyboardAvoidingView, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native'; import {connect} from "re
import React from 'react';
import {
Alert,
Button,
KeyboardAvoidingView,
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View
} from 'react-native';
import {connect} from "react-redux";
import {register} from "../redux/actions/Registration";
import {REGISTER} from "../redux/constants/ActionTypes";
import Loader from "./Loader";
const mapStateToProps = (state, ownProps) => {
return {
isLoggedIn: state.registration.isLoggedIn,
token: state.registration.token,
isLoading: state.common.isLoading,
error: state.common.error
};
};
const mapDispatchToProps = (dispatch) => {
return {
onRegister: (username, password, firstName, lastName) => {
dispatch(register(username, password, firstName, lastName));
}
}
};
@connect(mapStateToProps, mapDispatchToProps)
export default class RegistrationForm extends React.Component {
constructor(props) {
super(props);
this.state = {
route: REGISTER,
email: '',
password: '',
firstName: '',
lastName: '',
isLoading: false
}
}
register(e) {
this.props.onRegister(
this.state.email,
this.state.password,
this.state.firstName,
this.state.lastName
);
e.preventDefault();
}
componentDidUpdate() {
if (this.props.error != null && this.props.error !== undefined && this.props.error !== '') {
setTimeout(() => Alert.alert(this.props.error), 600);
}
}
render() {
const {isLoading} = this.props;
return (
<View style={styles.container}>
<KeyboardAvoidingView style={styles.registration_form} behaviour="padding">
<Loader loading={isLoading}/>
<TextInput style={styles.text_input} placeholder="First Name" placeholderTextColor="white"
underlineColorAndroid={'transparent'}
onChangeText={(text) => this.setState({firstName: text})}/>
<TextInput style={styles.text_input} placeholder="Last Name" placeholderTextColor="white"
underlineColorAndroid={'transparent'}
onChangeText={(text) => this.setState({lastName: text})}/>
<TextInput style={styles.text_input} placeholder="Email" placeholderTextColor="white"
underlineColorAndroid={'transparent'} keyboardType="email-address"
onChangeText={(text) => this.setState({email: text})}/>
<TextInput style={styles.text_input} placeholder="Password" placeholderTextColor="white"
underlineColorAndroid={'transparent'}
onChangeText={(text) => this.setState({password: text})}
secureTextEntry={true}/>
<TouchableOpacity style={styles.button} onPress={(e) => this.register(e)}>
<Text style={styles.btn_text}>Sign up</Text>
</TouchableOpacity>
<Button buttonStyle={{marginTop: 40}}
backgroundColor="transparent"
textStyle={{color: "#fff"}}
title="Login"
onPress={() => this.props.navigation.navigate('Login')}/>
</KeyboardAvoidingView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#36485f',
paddingLeft: 60,
paddingRight: 60
},
registration_form: {
alignSelf: 'stretch',
},
text_input: {
alignSelf: 'stretch',
height: 40,
marginBottom: 30,
color: '#fff',
borderBottomColor: '#f8f8f8',
borderBottomWidth: 1
},
button: {
alignSelf: 'stretch',
alignItems: 'center',
padding: 20,
backgroundColor: '#59cbbd',
marginTop: 30
},
btn_text: {
color: '#fff',
fontWeight: 'bold'
}
});
2) Registration.js
:
import {userService} from "../../service/UserService";
import {REGISTER} from "../constants/ActionTypes";
import {failed, loading} from "./Common";
export const register = (username, password, firstName, lastName) => {
return dispatch => {
dispatch(loading(true));
userService.register(username, password, firstName, lastName)
.then(resp => {
dispatch(loading(false));
dispatch({
type: REGISTER,
token: resp.json().token,
error: null
});
}
)
.catch(err => {
dispatch(loading(false));
dispatch(failed(err.message))
})
};
};
import {FAILED, LOADING, REGISTER} from "../constants/ActionTypes";
const defaultState = {
isLoading: false,
error: null
};
export default function reducer(state = defaultState, action) {
console.log('COMMON STATE: ', state);
console.log('COMMON ACTION: ', action);
switch (action.type) {
case LOADING:
return {
... state,
isLoading: action.isLoading
};
case FAILED:
return {
... state,
error: action.error
};
default:
return state;
}
}
import {FAILED, LOADING, REGISTER} from "../constants/ActionTypes";
const defaultState = {
isLoggedIn: false,
token: null
};
export default function reducer(state = defaultState, action) {
console.log('REGISTER STATE: ', state);
console.log('REGISTER ACTION: ', action);
switch (action.type) {
case REGISTER:
return Object.assign({}, state, {
isLoggedIn: action.isLoggedIn,
token: action.token
});
default:
return state;
}
}
import { combineReducers } from 'redux';
import registration from './Registration';
import login from './Login';
import common from './Common'
const rootReducer = combineReducers({
registration,
login,
common
});
export default rootReducer;
和减速器:
1) Common.js
:
import {userService} from "../../service/UserService";
import {REGISTER} from "../constants/ActionTypes";
import {failed, loading} from "./Common";
export const register = (username, password, firstName, lastName) => {
return dispatch => {
dispatch(loading(true));
userService.register(username, password, firstName, lastName)
.then(resp => {
dispatch(loading(false));
dispatch({
type: REGISTER,
token: resp.json().token,
error: null
});
}
)
.catch(err => {
dispatch(loading(false));
dispatch(failed(err.message))
})
};
};
import {FAILED, LOADING, REGISTER} from "../constants/ActionTypes";
const defaultState = {
isLoading: false,
error: null
};
export default function reducer(state = defaultState, action) {
console.log('COMMON STATE: ', state);
console.log('COMMON ACTION: ', action);
switch (action.type) {
case LOADING:
return {
... state,
isLoading: action.isLoading
};
case FAILED:
return {
... state,
error: action.error
};
default:
return state;
}
}
import {FAILED, LOADING, REGISTER} from "../constants/ActionTypes";
const defaultState = {
isLoggedIn: false,
token: null
};
export default function reducer(state = defaultState, action) {
console.log('REGISTER STATE: ', state);
console.log('REGISTER ACTION: ', action);
switch (action.type) {
case REGISTER:
return Object.assign({}, state, {
isLoggedIn: action.isLoggedIn,
token: action.token
});
default:
return state;
}
}
import { combineReducers } from 'redux';
import registration from './Registration';
import login from './Login';
import common from './Common'
const rootReducer = combineReducers({
registration,
login,
common
});
export default rootReducer;
2) Registration.js
:
import {userService} from "../../service/UserService";
import {REGISTER} from "../constants/ActionTypes";
import {failed, loading} from "./Common";
export const register = (username, password, firstName, lastName) => {
return dispatch => {
dispatch(loading(true));
userService.register(username, password, firstName, lastName)
.then(resp => {
dispatch(loading(false));
dispatch({
type: REGISTER,
token: resp.json().token,
error: null
});
}
)
.catch(err => {
dispatch(loading(false));
dispatch(failed(err.message))
})
};
};
import {FAILED, LOADING, REGISTER} from "../constants/ActionTypes";
const defaultState = {
isLoading: false,
error: null
};
export default function reducer(state = defaultState, action) {
console.log('COMMON STATE: ', state);
console.log('COMMON ACTION: ', action);
switch (action.type) {
case LOADING:
return {
... state,
isLoading: action.isLoading
};
case FAILED:
return {
... state,
error: action.error
};
default:
return state;
}
}
import {FAILED, LOADING, REGISTER} from "../constants/ActionTypes";
const defaultState = {
isLoggedIn: false,
token: null
};
export default function reducer(state = defaultState, action) {
console.log('REGISTER STATE: ', state);
console.log('REGISTER ACTION: ', action);
switch (action.type) {
case REGISTER:
return Object.assign({}, state, {
isLoggedIn: action.isLoggedIn,
token: action.token
});
default:
return state;
}
}
import { combineReducers } from 'redux';
import registration from './Registration';
import login from './Login';
import common from './Common'
const rootReducer = combineReducers({
registration,
login,
common
});
export default rootReducer;
3) Index.js
:
import {userService} from "../../service/UserService";
import {REGISTER} from "../constants/ActionTypes";
import {failed, loading} from "./Common";
export const register = (username, password, firstName, lastName) => {
return dispatch => {
dispatch(loading(true));
userService.register(username, password, firstName, lastName)
.then(resp => {
dispatch(loading(false));
dispatch({
type: REGISTER,
token: resp.json().token,
error: null
});
}
)
.catch(err => {
dispatch(loading(false));
dispatch(failed(err.message))
})
};
};
import {FAILED, LOADING, REGISTER} from "../constants/ActionTypes";
const defaultState = {
isLoading: false,
error: null
};
export default function reducer(state = defaultState, action) {
console.log('COMMON STATE: ', state);
console.log('COMMON ACTION: ', action);
switch (action.type) {
case LOADING:
return {
... state,
isLoading: action.isLoading
};
case FAILED:
return {
... state,
error: action.error
};
default:
return state;
}
}
import {FAILED, LOADING, REGISTER} from "../constants/ActionTypes";
const defaultState = {
isLoggedIn: false,
token: null
};
export default function reducer(state = defaultState, action) {
console.log('REGISTER STATE: ', state);
console.log('REGISTER ACTION: ', action);
switch (action.type) {
case REGISTER:
return Object.assign({}, state, {
isLoggedIn: action.isLoggedIn,
token: action.token
});
default:
return state;
}
}
import { combineReducers } from 'redux';
import registration from './Registration';
import login from './Login';
import common from './Common'
const rootReducer = combineReducers({
registration,
login,
common
});
export default rootReducer;
当我第一次单击Register组件上的“注册”按钮时(一切正常),它会显示一个微调器,然后显示一个警报
当我第二次(或第三次)单击“注册”按钮时,它也会显示一个微调器,然后它会逐个打开两个警报
我期望的是:在每次单击应用程序时,应该只显示一个警报
在控制台中,我看到以下输出:
10:27:07 PM: REGISTER STATE: Object {
10:27:07 PM: "isLoggedIn": false,
10:27:07 PM: "token": null,
10:27:07 PM: }
10:27:07 PM: REGISTER ACTION: Object {
10:27:07 PM: "isLoading": false,
10:27:07 PM: "type": "LOADING",
10:27:07 PM: }
10:27:07 PM: LOGIN STATE: Object {
10:27:07 PM: "isLoggedIn": false,
10:27:07 PM: "token": null,
10:27:07 PM: }
10:27:07 PM: LOGIN ACTION: Object {
10:27:07 PM: "isLoading": false,
10:27:07 PM: "type": "LOADING",
10:27:07 PM: }
10:27:07 PM: COMMON STATE: Object {
10:27:07 PM: "error": "Network request failed",
10:27:07 PM: "isLoading": true,
10:27:07 PM: }
10:27:07 PM: COMMON ACTION: Object {
10:27:07 PM: "isLoading": false,
10:27:07 PM: "type": "LOADING",
10:27:07 PM: }
10:27:07 PM: REGISTER STATE: Object {
10:27:07 PM: "isLoggedIn": false,
10:27:07 PM: "token": null,
10:27:07 PM: }
10:27:07 PM: REGISTER ACTION: Object {
10:27:07 PM: "error": "Network request failed",
10:27:07 PM: "type": "FAILED",
10:27:07 PM: }
10:27:07 PM: LOGIN STATE: Object {
10:27:07 PM: "isLoggedIn": false,
10:27:07 PM: "token": null,
10:27:07 PM: }
10:27:07 PM: LOGIN ACTION: Object {
10:27:07 PM: "error": "Network request failed",
10:27:07 PM: "type": "FAILED",
10:27:07 PM: }
10:27:07 PM: COMMON STATE: Object {
10:27:07 PM: "error": "Network request failed",
10:27:07 PM: "isLoading": false,
10:27:07 PM: }
10:27:07 PM: COMMON ACTION: Object {
10:27:07 PM: "error": "Network request failed",
10:27:07 PM: "type": "FAILED",
10:27:07 PM: }
所以减速机被调用了两次。如何解决这个问题?或者如何防止打开第二个警报弹出窗口
更新。
您可以在GitHub上找到该项目的源代码:
更新2。
从@Chase DeAnda更改后,它看起来是这样的:
在
注册
功能中,您需要检查道具
以确定用户是否已注册:
register(e) {
e.preventDefault();
if (!this.props.token) {
this.props.onRegister(
this.state.email,
this.state.password,
this.state.firstName,
this.state.lastName
);
} else {
// User already registered
// Redirect to login page
}
}
编辑
啊,好吧,我想我现在明白问题了。您正在使用componentdiddupdate
生命周期方法。此方法在每次一个状态或道具被传递给它时被调用,而不一定只有在它们发生更改时才调用。您遇到的问题是,您没有检查此.props.error
是否与您已经显示的第一个错误不同。将其更改为:
componentDidUpdate(prevProps,prevState) {
if(prevProps.error !== this.props.error && this.props.error){
setTimeout(() => Alert.alert(this.props.error), 600);
}
}
由于您有多个还原程序对失败的XHR请求作出反应,因此您的组件将从多个操作中获得传递给它的道具。您需要确保仅当错误与以前传入的错误不同时才显示错误。我不需要重定向。寄存器函数只被调用了一次,看起来不错(对我来说)。问题在于redux,因为它多次调用reducer寄存器api调用失败,如图所示:
register ACTION:Object{“error”:“Network request failed”,“type”:“failed”}
是的,我知道。如果出现错误,应打开警报。问题是它会打开2个警报,而不是1个对您的代码进行测试-这没有帮助。现在,当我第一次点击按钮时,它会显示警报,但当我第二次点击时——它在allOkay时不会显示警报,您的要求非常不清楚。现在我不知道你在期待什么。我希望我能提供的帮助。祝你好运