Redux 操作有效但 reducer 无效
Redux action is working but reducer is not
不确定为什么我的减速器不工作。我的操作文件中有一个 console.log。因此,例如,当我测试失败的登录尝试时,我看到控制台登录了 loginFromAPI 函数和 loginFailure 函数 (参见下面的 actions.js)。这些正在输出预期的内容。但出于某种原因,登录减速器无法正常工作。当我在 loginReducer.js 中 console.log 时,我什么也看不到。这告诉我,虽然 action 有效,但 reducer 无效。我的 redux 道具不会从初始状态改变(我已经通过输出到控制台进行了检查)。如果能帮助解决这个问题,我将不胜感激。
文件结构如下:
app
reducers
loginReducer.js
rootReducer.js
screens
Login
Login.js
index.js
actions.js
store
configureStore.js
App.js
actionTypes.js
文件configureStore.js
import { createStore, applyMiddleware } from 'redux';
import app from '../reducers/rootReducer';
import thunk from 'redux-thunk';
import {createLogger} from 'redux-logger';
export default function configureStore() {
const logger = createLogger();
let store = createStore(app, applyMiddleware(thunk, logger));
return store;
}
文件rootReducer.js
import { combineReducers } from 'redux';
import loginReducer, * as fromLogin from './loginReducer';
export default combineReducers({
login: loginReducer
})
export const getLogin = (state) => fromLogin.getLogin(state.login)
文件loginReducer.js
import { LOGIN, LOGIN_SUCCESS, LOGIN_FAILURE } from '../actionTypes';
const initialState = {
user: {},
isFetching: false,
isLoggedIn: false,
error: false,
errorMessage: "",
}
export default function loginReducer(state = initialState, action) {
console.log(action); <-- I do not see this
switch(action.type) {
case LOGIN:
return {
...state,
isFetching: true,
user: {}
}
case LOGIN_SUCCESS:
return {
...state,
isFetching: false,
isLoggedIn: true,
user: action.user
}
case LOGIN_FAILURE:
return {
isFetching: false,
error: true,
errorMessage: action.errorMessage
}
default:
return state
}
}
export const getLogin = (state) => ({
user: state.user,
isFetching: state.isFetching,
isLoggedIn: state.isLoggedIn,
error: state.error,
errorMessage: state.errorMessage
})
文件actions.js
import { LOGIN, LOGIN_SUCCESS, LOGIN_FAILURE } from '../../actionTypes';
import axios from 'axios';
export function loginFromAPI(loginInfo) {
login();
axios({
method: 'POST',
url: 'SOME_URL_GOES_HERE',
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
data: JSON.stringify({
email: loginInfo.email,
password: loginInfo.password
}),
})
.then((response) => {
loginSuccess(response.data);
console.log(response.data);
})
.catch(error => {
switch(error.response.status) {
case 404:
console.log("No user with that email.");
loginFailure("No user with that email.")
break;
case 401:
console.log("Invalid password.");
loginFailure("Invalid password.")
break;
default:
console.log("There was an error loggin in");
loginFailure("There was an error loggin in");
break;
}
})
}
function login() {
return {
type: LOGIN,
}
}
function loginSuccess(user) {
return {
type: LOGIN_SUCCESS,
user
}
}
function loginFailure(errorMessage) {
console.log(errorMessage); <-- I see this output
return {
type: LOGIN_FAILURE,
errorMessage
}
}
文件index.js
import Login from './Login';
import {connect} from 'react-redux';
import * as actions from './actions';
import {getLogin} from '../../reducers/rootReducer';
const mapStateToProps = (state) => ({
...getLogin(state),
})
const mapDispatchToProps = () => ({
...actions
})
export default connect(mapStateToProps, mapDispatchToProps)(Login)
文件Login.js
export default class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: null,
password: null,
}
this.login = this.login.bind(this);
}
login() {
this.props.loginFromAPI({email: this.state.email, password: this.state.password});
console.log(this.props) <-- This never changes but does show up
}
render() {
let error = this.props.error
return (
<ImageBackground source={require('../../assets/Signup.png')} style={styles.container}>
<View style={styles.content}>
...
Some text input Stuff
...
{error &&
<Text>{this.props.errorMessage}</Text>
}
<Button
onPress={this.login}
/>
...
);
}
}
您没有在 mapDispatchToProps 中调度您的操作。
https://redux.js.org/basics/actions
因此,要使操作生效,您需要 return 一个包含与您的 reducer switch case 匹配的键 "type" 的对象。
{
类型:"MY_ACTION_TYPE"
}
但是您的函数 loginFromAPI 是异步的,因此您不能只 return 它的一个对象。要解决这个问题,您可以使用 redux 中间件。
两个最流行的是 redux-thunk 和 redux-saga。 redux-thunk 就简单多了。
Thunk 示例:
export function loginFromAPI(loginInfo) {
return function (dispatch) {
dispatch(login())
login()
.then(res => dispatch(loginSuccess()) // the action you want to dispatch
}
}
不确定为什么我的减速器不工作。我的操作文件中有一个 console.log。因此,例如,当我测试失败的登录尝试时,我看到控制台登录了 loginFromAPI 函数和 loginFailure 函数 (参见下面的 actions.js)。这些正在输出预期的内容。但出于某种原因,登录减速器无法正常工作。当我在 loginReducer.js 中 console.log 时,我什么也看不到。这告诉我,虽然 action 有效,但 reducer 无效。我的 redux 道具不会从初始状态改变(我已经通过输出到控制台进行了检查)。如果能帮助解决这个问题,我将不胜感激。
文件结构如下:
app
reducers
loginReducer.js
rootReducer.js
screens
Login
Login.js
index.js
actions.js
store
configureStore.js
App.js
actionTypes.js
文件configureStore.js
import { createStore, applyMiddleware } from 'redux';
import app from '../reducers/rootReducer';
import thunk from 'redux-thunk';
import {createLogger} from 'redux-logger';
export default function configureStore() {
const logger = createLogger();
let store = createStore(app, applyMiddleware(thunk, logger));
return store;
}
文件rootReducer.js
import { combineReducers } from 'redux';
import loginReducer, * as fromLogin from './loginReducer';
export default combineReducers({
login: loginReducer
})
export const getLogin = (state) => fromLogin.getLogin(state.login)
文件loginReducer.js
import { LOGIN, LOGIN_SUCCESS, LOGIN_FAILURE } from '../actionTypes';
const initialState = {
user: {},
isFetching: false,
isLoggedIn: false,
error: false,
errorMessage: "",
}
export default function loginReducer(state = initialState, action) {
console.log(action); <-- I do not see this
switch(action.type) {
case LOGIN:
return {
...state,
isFetching: true,
user: {}
}
case LOGIN_SUCCESS:
return {
...state,
isFetching: false,
isLoggedIn: true,
user: action.user
}
case LOGIN_FAILURE:
return {
isFetching: false,
error: true,
errorMessage: action.errorMessage
}
default:
return state
}
}
export const getLogin = (state) => ({
user: state.user,
isFetching: state.isFetching,
isLoggedIn: state.isLoggedIn,
error: state.error,
errorMessage: state.errorMessage
})
文件actions.js
import { LOGIN, LOGIN_SUCCESS, LOGIN_FAILURE } from '../../actionTypes';
import axios from 'axios';
export function loginFromAPI(loginInfo) {
login();
axios({
method: 'POST',
url: 'SOME_URL_GOES_HERE',
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
data: JSON.stringify({
email: loginInfo.email,
password: loginInfo.password
}),
})
.then((response) => {
loginSuccess(response.data);
console.log(response.data);
})
.catch(error => {
switch(error.response.status) {
case 404:
console.log("No user with that email.");
loginFailure("No user with that email.")
break;
case 401:
console.log("Invalid password.");
loginFailure("Invalid password.")
break;
default:
console.log("There was an error loggin in");
loginFailure("There was an error loggin in");
break;
}
})
}
function login() {
return {
type: LOGIN,
}
}
function loginSuccess(user) {
return {
type: LOGIN_SUCCESS,
user
}
}
function loginFailure(errorMessage) {
console.log(errorMessage); <-- I see this output
return {
type: LOGIN_FAILURE,
errorMessage
}
}
文件index.js
import Login from './Login';
import {connect} from 'react-redux';
import * as actions from './actions';
import {getLogin} from '../../reducers/rootReducer';
const mapStateToProps = (state) => ({
...getLogin(state),
})
const mapDispatchToProps = () => ({
...actions
})
export default connect(mapStateToProps, mapDispatchToProps)(Login)
文件Login.js
export default class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: null,
password: null,
}
this.login = this.login.bind(this);
}
login() {
this.props.loginFromAPI({email: this.state.email, password: this.state.password});
console.log(this.props) <-- This never changes but does show up
}
render() {
let error = this.props.error
return (
<ImageBackground source={require('../../assets/Signup.png')} style={styles.container}>
<View style={styles.content}>
...
Some text input Stuff
...
{error &&
<Text>{this.props.errorMessage}</Text>
}
<Button
onPress={this.login}
/>
...
);
}
}
您没有在 mapDispatchToProps 中调度您的操作。 https://redux.js.org/basics/actions
因此,要使操作生效,您需要 return 一个包含与您的 reducer switch case 匹配的键 "type" 的对象。
{ 类型:"MY_ACTION_TYPE" }
但是您的函数 loginFromAPI 是异步的,因此您不能只 return 它的一个对象。要解决这个问题,您可以使用 redux 中间件。
两个最流行的是 redux-thunk 和 redux-saga。 redux-thunk 就简单多了。
Thunk 示例:
export function loginFromAPI(loginInfo) {
return function (dispatch) {
dispatch(login())
login()
.then(res => dispatch(loginSuccess()) // the action you want to dispatch
}
}