使用 React,为什么 redux saga 不拦截动作?
Using React, why is redux saga not intercepting the action?
我是 redux-saga 的新手,我正在尝试让 simple demo 工作,进行 API 调用并将响应数据发送到 reducer,以便将其保存到店铺。据我了解,redux-saga 流程应该按如下方式工作。
- 组件调用动作创建器
- 动作创建者然后使用特定类型发出动作
- watcher sagas 全部监听发出的任何动作并拦截它正在监听的动作。然后它调用适当的 worker saga。
- worker saga 进行 API 调用并使用操作类型和负载将操作分派给 reducer。
- reducer 侦听任何已分派的操作,如果匹配,则使用提供的数据更新存储中的状态。
我已经制定了我的代码来遵循该流程,但事情不太正常。让我展示我的代码,然后我会详细说明问题。
components/PostsIndex.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchPosts } from '../actions';
class PostsIndex extends Component {
componentDidMount() {
this.props.fetchPosts();
}
renderPosts() {
console.log(this.props.posts);
}
render() {
return (
<div>
{this.renderPosts()}
</div>
);
}
}
const mapStateToProps = state => ({
posts: state.posts
});
export default connect(mapStateToProps, { fetchPosts })(PostsIndex);
actions/index.js
import axios from 'axios';
export const FETCH_POSTS = 'FETCH_POSTS';
export const fetchPosts = () => {
console.log('fetchPosts() in actions');
return {
type: FETCH_POSTS
};
};
sagas/index.js
import 'regenerator-runtime/runtime';
import { call, put, takeEvery, takeLatest } from 'redux-saga/effects';
import axios from 'axios';
import { FETCH_POSTS } from '../actions';
const ROOT_URL = 'https://reduxblog.herokuapp.com/api';
const API_KEY = '?key=asdsd1234';
// Watcher sagas
// Listen for an action and run the appropriate Worker saga
export function* watchFetchPosts() {
yield takeEvery(FETCH_POSTS, workFetchPosts);
}
// Worker sagas
// Respond to the actions that are caught by the watcher sagas
export function* workFetchPosts() {
try {
console.log('workFetchPosts() in sagas');
// Try to call the API
console.log('Attempting to call the posts API');
const uri = `${ROOT_URL}/posts${API_KEY}`;
const response = yield call(axios.get, uri);
console.log('Fetched Posts Response in saga worker: ', response);
yield put({
type: FETCH_POSTS,
payload: response
});
} catch (error) {
// Act on the error
console.log('Request failed! Could not fetch posts.');
console.log(error);
}
}
// Root sagas
// Single entry point to start all sagas at once
export default function* rootSaga() {
console.log('redux saga is running');
yield [watchFetchPosts()];
}
reducers/PostsReducer.js
import { mapKeys } from 'lodash';
import { FETCH_POSTS } from '../actions';
export default (state = {}, action) => {
switch (action.type) {
case FETCH_POSTS:
console.log(action);
// Create a new state object that uses an AJAX request response and grabs the 'id' property from each object in the response to use as its key
return mapKeys(action.payload.data, 'id');
}
return state;
};
似乎 reducer 仍在接收发出的动作,这是错误的。我确实注意到,如果我 运行 AJAX 在 action creator 中调用,那么 saga 将 运行,但是 saga 应该拦截 action creator 和 reducer 之间的通信,所以有些东西设置不正确。有什么想法吗?
可以在 https://stackblitz.com/edit/react-redux-saga-demo 编辑我的工作流程的完整环境。在那里可能更容易看到问题。
Sagas 不会 阻止动作到达减速器。 saga 中间件显式地做相当于:
next(action); // pass the action onwards to the reducers
processSagas(action);
所以,reducers 总是先看到一个动作,saga 行为会在之后执行。
另一个问题是您似乎在尝试使用相同的操作类型来触发 saga 中的提取行为,和 在 reducer 中处理结果。我发现,如果您正在使用 sagas,您通常会有一些 "signals" 旨在触发 saga 行为的操作,以及其他旨在由 reducer 实际处理和更新状态的操作。因此,在您的情况下,我建议使用 "FETCH_POSTS"
作为开始获取的信号,然后让 saga 在接收到数据后分派 "FETCH_POSTS_SUCCESS"
并让 reducer 响应该操作。 (而且,在注意到你有 StackBlitz 示例后,我确认仅将结果发送为 "FETCH_POSTS_SUCCESS"
确实如我预期的那样工作。)
我是 redux-saga 的新手,我正在尝试让 simple demo 工作,进行 API 调用并将响应数据发送到 reducer,以便将其保存到店铺。据我了解,redux-saga 流程应该按如下方式工作。
- 组件调用动作创建器
- 动作创建者然后使用特定类型发出动作
- watcher sagas 全部监听发出的任何动作并拦截它正在监听的动作。然后它调用适当的 worker saga。
- worker saga 进行 API 调用并使用操作类型和负载将操作分派给 reducer。
- reducer 侦听任何已分派的操作,如果匹配,则使用提供的数据更新存储中的状态。
我已经制定了我的代码来遵循该流程,但事情不太正常。让我展示我的代码,然后我会详细说明问题。
components/PostsIndex.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchPosts } from '../actions';
class PostsIndex extends Component {
componentDidMount() {
this.props.fetchPosts();
}
renderPosts() {
console.log(this.props.posts);
}
render() {
return (
<div>
{this.renderPosts()}
</div>
);
}
}
const mapStateToProps = state => ({
posts: state.posts
});
export default connect(mapStateToProps, { fetchPosts })(PostsIndex);
actions/index.js
import axios from 'axios';
export const FETCH_POSTS = 'FETCH_POSTS';
export const fetchPosts = () => {
console.log('fetchPosts() in actions');
return {
type: FETCH_POSTS
};
};
sagas/index.js
import 'regenerator-runtime/runtime';
import { call, put, takeEvery, takeLatest } from 'redux-saga/effects';
import axios from 'axios';
import { FETCH_POSTS } from '../actions';
const ROOT_URL = 'https://reduxblog.herokuapp.com/api';
const API_KEY = '?key=asdsd1234';
// Watcher sagas
// Listen for an action and run the appropriate Worker saga
export function* watchFetchPosts() {
yield takeEvery(FETCH_POSTS, workFetchPosts);
}
// Worker sagas
// Respond to the actions that are caught by the watcher sagas
export function* workFetchPosts() {
try {
console.log('workFetchPosts() in sagas');
// Try to call the API
console.log('Attempting to call the posts API');
const uri = `${ROOT_URL}/posts${API_KEY}`;
const response = yield call(axios.get, uri);
console.log('Fetched Posts Response in saga worker: ', response);
yield put({
type: FETCH_POSTS,
payload: response
});
} catch (error) {
// Act on the error
console.log('Request failed! Could not fetch posts.');
console.log(error);
}
}
// Root sagas
// Single entry point to start all sagas at once
export default function* rootSaga() {
console.log('redux saga is running');
yield [watchFetchPosts()];
}
reducers/PostsReducer.js
import { mapKeys } from 'lodash';
import { FETCH_POSTS } from '../actions';
export default (state = {}, action) => {
switch (action.type) {
case FETCH_POSTS:
console.log(action);
// Create a new state object that uses an AJAX request response and grabs the 'id' property from each object in the response to use as its key
return mapKeys(action.payload.data, 'id');
}
return state;
};
似乎 reducer 仍在接收发出的动作,这是错误的。我确实注意到,如果我 运行 AJAX 在 action creator 中调用,那么 saga 将 运行,但是 saga 应该拦截 action creator 和 reducer 之间的通信,所以有些东西设置不正确。有什么想法吗?
可以在 https://stackblitz.com/edit/react-redux-saga-demo 编辑我的工作流程的完整环境。在那里可能更容易看到问题。
Sagas 不会 阻止动作到达减速器。 saga 中间件显式地做相当于:
next(action); // pass the action onwards to the reducers
processSagas(action);
所以,reducers 总是先看到一个动作,saga 行为会在之后执行。
另一个问题是您似乎在尝试使用相同的操作类型来触发 saga 中的提取行为,和 在 reducer 中处理结果。我发现,如果您正在使用 sagas,您通常会有一些 "signals" 旨在触发 saga 行为的操作,以及其他旨在由 reducer 实际处理和更新状态的操作。因此,在您的情况下,我建议使用 "FETCH_POSTS"
作为开始获取的信号,然后让 saga 在接收到数据后分派 "FETCH_POSTS_SUCCESS"
并让 reducer 响应该操作。 (而且,在注意到你有 StackBlitz 示例后,我确认仅将结果发送为 "FETCH_POSTS_SUCCESS"
确实如我预期的那样工作。)