React redux api 每 x 秒轮询一次
React redux api polling every x seconds
我已经开始工作了,但我需要更多 'best practice way'。
它使用 https://icanhazdadjoke api 来显示每 x 秒更新一次的随机笑话。有更好的方法吗?
最终我想添加停止、启动、重置功能,但感觉这种方式可能不是最好的。
我可以使用任何中间件吗?
Redux 动作
// action types
import axios from 'axios';
export const FETCH_JOKE = 'FETCH_JOKE';
export const FETCH_JOKE_SUCCESS = 'FETCH_JOKE_SUCCESS';
export const FETCH_JOKE_FAILURE = 'FETCH_JOKE_FAILURE';
function fetchJoke() {
return {
type: FETCH_JOKE
};
}
function fetchJokeSuccess(data) {
return {
type: FETCH_JOKE_SUCCESS,
data
};
}
function fetchJokeFail(error) {
return {
type: FETCH_JOKE_FAILURE,
error
};
}
export function fetchJokeCall(){
return function(dispatch){
dispatch(fetchJoke());
return axios.get('https://icanhazdadjoke.com', { headers: { 'Accept': 'application/json' }})
.then(function(result){
dispatch(fetchJokeSuccess(result.data))
})
.catch(error => dispatch(fetchJokeFail(error)));
}
}
Redux 减速器
import {combineReducers} from 'redux';
import {FETCH_JOKE, FETCH_JOKE_SUCCESS, FETCH_JOKE_FAILURE} from '../actions';
const defaultStateList = {
isFetching: false,
items:[],
error:{}
};
const joke = (state = defaultStateList, action) => {
switch (action.type){
case FETCH_JOKE:
return {...state, isFetching:true};
case FETCH_JOKE_SUCCESS:
return {...state, isFetching:false, items:action.data};
case FETCH_JOKE_FAILURE:
return {...state, isFetching:false, error:action.data};
default:
return state;
}
};
const rootReducer = combineReducers({
joke
});
export default rootReducer;
笑话组件
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { fetchJokeCall } from '../actions';
class Joke extends Component {
componentDidMount() {
this.timer = setInterval(()=> this.props.fetchJokeCall(), 1000);
}
componentWillUnmount() {
clearInterval(this.timer)
this.timer = null;
}
render() {
return (
<div>
{this.props.joke.joke}
</div>
);
}
}
Joke.propTypes = {
fetchJokeCall: PropTypes.func,
joke: PropTypes.array.isRequired
};
function mapStateToProps(state) {
return {
joke: state.joke.items,
isfetching: state.joke.isFetching
};
}
export default connect(mapStateToProps, { fetchJokeCall })(Joke);
redux-saga
很棒,我一直在使用 redux。它提供了一个很好的 api 来做延迟、轮询、节流、竞争条件、任务取消等事情。所以使用 redux-saga,你可以添加一个 whcih whcih 将继续池化的观察者
function* pollSagaWorker(action) {
while (true) {
try {
const { data } = yield call(() => axios({ url: ENDPOINT }));
yield put(getDataSuccessAction(data));
yield call(delay, 4000);
} catch (err) {
yield put(getDataFailureAction(err));
}
}
}
Redux-Sagas 更好,我们也在我们的应用程序中使用它,这就是您可以使用 Redux-Sagas 进行轮询的方式
只是为了让您了解如何做到这一点,您还需要了解 Redux-Sagas 的工作原理
操作
export const FETCH_JOKE = 'FETCH_JOKE';
export const FETCH_JOKE_SUCCESS = 'FETCH_JOKE_SUCCESS';
export const FETCH_JOKE_FAILURE = 'FETCH_JOKE_FAILURE';
export const START_POLLING = 'START_POLLING';
export const STOP_POLLING = 'STOP_POLLING';
function startPolling() {
return {
type: START_POLLING
};
}
function stopPolling() {
return {
type: STOP_POLLING
};
}
function fetchJoke() {
return {
type: FETCH_JOKE
};
}
function fetchJokeSuccess(data) {
return {
type: FETCH_JOKE_SUCCESS,
data
};
}
function fetchJokeFail(error) {
return {
type: FETCH_JOKE_FAILURE,
error
};
}
减速器
import {combineReducers} from 'redux';
import {FETCH_JOKE, FETCH_JOKE_SUCCESS, FETCH_JOKE_FAILURE, START_POLLING, STOP_POLLING } from '../actions';
const defaultStateList = {
isFetching: false,
items:[],
error:{},
isPolling: false,
};
const joke = (state = defaultStateList, action) => {
switch (action.type){
case FETCH_JOKE:
return {...state, isFetching:true};
case FETCH_JOKE_SUCCESS:
return {...state, isFetching:false, items:action.data};
case FETCH_JOKE_FAILURE:
return {...state, isFetching:false, error:action.data};
case START_POLLING:
return {...state, isPolling: true};
case STOP_POLLING:
return {...state, isPolling: false};
default:
return state;
}
};
const rootReducer = combineReducers({
joke
});
export default rootReducer;
传奇
import { call, put, takeEvery, takeLatest, take, race } from 'redux-saga/effects'
import {FETCH_JOKE, FETCH_JOKE_SUCCESS, FETCH_JOKE_FAILURE, START_POLLING, STOP_POLLING } from '../actions';
import axios from 'axios';
function delay(duration) {
const promise = new Promise(resolve => {
setTimeout(() => resolve(true), duration)
})
return promise
}
function* fetchJokes(action) {
while (true) {
try {
const { data } = yield call(() => axios({ url: ENDPOINT }))
yield put({ type: FETCH_JOKE_SUCCESS, data: data })
yield call(delay, 5000)
} catch (e) {
yield put({ type: FETCH_JOKE_FAILURE, message: e.message })
}
}
}
function* watchPollJokesSaga() {
while (true) {
const data = yield take(START_POLLING)
yield race([call(fetchJokes, data), take(STOP_POLLING)])
}
}
export default function* root() {
yield [watchPollJokesSaga()]
}
您也可以使用 Redux-Observable,如果您想了解更多,请阅读此 article
我一直在处理几乎相同的问题,只是我不关心开始和停止投票。出于某种原因,while 循环一直冻结我的应用程序,所以我放弃了它,而是像这样设置我的传奇。
import { all, takeLatest, call, put } from 'redux-saga/effects';
import axios from 'axios';
import { API_CALL_REQUEST, API_CALL_SUCCESS, API_CALL_FAILURE, API_CALL_FETCHED } from
'../actions/giphy';
function apiFetch() {
let randomWord = require('random-words');
let API_ENDPOINT = `https://api.giphy.com/v1/gifs/search?
api_key=MYKEY&q=${randomWord()}&limit=12`;
return axios({
method: "get",
url: API_ENDPOINT
});
}
export function* fetchImages() {
try {
const res = yield call(apiFetch)
const images = yield res.data
yield put({type: API_CALL_SUCCESS, images})
} catch (e) {
yield put({type: API_CALL_FAILURE, e})
console.log('Error fetching giphy data')
}
}
export default function* giphySaga() {
yield all([
takeLatest(API_CALL_REQUEST, fetchImages),
]);
}
然后在我的组件中添加了这个。
componentDidMount() {
this.interval = setInterval(() => {
this.props.dispatch({type: 'API_CALL_REQUEST'});
}, 5000);
}
componentWillUnmount() {
clearInterval(this.interval)
}
它正在运行,但希望得到一些关于如何改进它的反馈。
这是穷人的方式。我认为这不是最好的方法,但它不需要任何额外的库。
操作
// action types
import axios from 'axios';
export const START_POLLING_JOKE = 'START_POLLING_JOKE';
export const STOP_POLLING_JOKE = 'STOP_POLLING_JOKE';
export const FETCH_JOKE = 'FETCH_JOKE';
export const FETCH_JOKE_SUCCESS = 'FETCH_JOKE_SUCCESS';
export const FETCH_JOKE_FAILURE = 'FETCH_JOKE_FAILURE';
const defaultPollingInterval = 60000
function startPollingJoke(interval = defaultPollingInterval) {
return function (dispatch) {
const fetch = () => dispatch(fetchJoke())
dispatch({
type: START_POLLING_JOKE,
interval,
fetch,
})
}
}
function stopPollingJoke() {
return {
type: STOP_POLLING_JOKE
}
}
function fetchJoke() {
return {
type: FETCH_JOKE
};
}
function fetchJokeSuccess(data) {
return {
type: FETCH_JOKE_SUCCESS,
data
};
}
function fetchJokeFail(error) {
return {
type: FETCH_JOKE_FAILURE,
error
};
}
export function pollJokeCall(interval = defaultPollingInterval) {
return function (dispatch) {
dispatch(fetchJoke())
dispatch(startPollingJoke(interval))
}
}
export function fetchJokeCall() {
return function(dispatch){
dispatch(fetchJoke());
return axios.get('https://icanhazdadjoke.com', { headers: { 'Accept': 'application/json' }})
.then(function(result){
dispatch(fetchJokeSuccess(result.data))
})
.catch(error => dispatch(fetchJokeFail(error)));
}
}
减速器
import {combineReducers} from 'redux';
import {
START_POLLING_JOKE,
STOP_POLLING_JOKE,
FETCH_JOKE,
FETCH_JOKE_SUCCESS,
FETCH_JOKE_FAILURE,
} from '../actions';
const defaultStateList = {
isFetching: false,
items:[],
error:{},
pollingId: null,
polling: false,
};
const joke = (state = defaultStateList, action) => {
switch (action.type){
case START_POLLING_JOKE:
clearInterval(state.pollingId)
return {
...state,
polling: true,
pollingId: setInterval(action.fetch, action.interval),
}
}
case STOP_POLLING_JOKE:
clearInterval(state.pollingId)
return {...state, polling: false, pollingId: null}
case FETCH_JOKE:
return {...state, isFetching:true};
case FETCH_JOKE_SUCCESS:
return {...state, isFetching:false, items:action.data};
case FETCH_JOKE_FAILURE:
return {...state, isFetching:false, error:action.data};
default:
return state;
}
};
const rootReducer = combineReducers({
joke
});
export default rootReducer;
组件(可能有错误,因为我不习惯 class 组件)
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { pollJokeCall, stopPollingJoke } from '../actions';
class Joke extends Component {
componentDidMount() {
this.props.pollJokeCall()
}
componentWillUnmount() {
this.props.stopPollingJoke()
}
render() {
return (
<div>
{this.props.joke.joke}
</div>
);
}
}
Joke.propTypes = {
pollJokeCall: PropTypes.func,
stopPollingJoke: PropTypes.func,
joke: PropTypes.array.isRequired,
};
function mapStateToProps(state) {
return {
joke: state.joke.items,
isfetching: state.joke.isFetching
};
}
export default connect(mapStateToProps, { pollJokeCall, stopPollingJoke })(Joke);
我制作了一个小的(5kb gzipped)助手来创建基于 redux-thunk 存储的轮询。这个想法是有一个逻辑来防止两次注册相同的轮询,在迭代之间有回调等等。
我已经开始工作了,但我需要更多 'best practice way'。
它使用 https://icanhazdadjoke api 来显示每 x 秒更新一次的随机笑话。有更好的方法吗?
最终我想添加停止、启动、重置功能,但感觉这种方式可能不是最好的。
我可以使用任何中间件吗?
Redux 动作
// action types
import axios from 'axios';
export const FETCH_JOKE = 'FETCH_JOKE';
export const FETCH_JOKE_SUCCESS = 'FETCH_JOKE_SUCCESS';
export const FETCH_JOKE_FAILURE = 'FETCH_JOKE_FAILURE';
function fetchJoke() {
return {
type: FETCH_JOKE
};
}
function fetchJokeSuccess(data) {
return {
type: FETCH_JOKE_SUCCESS,
data
};
}
function fetchJokeFail(error) {
return {
type: FETCH_JOKE_FAILURE,
error
};
}
export function fetchJokeCall(){
return function(dispatch){
dispatch(fetchJoke());
return axios.get('https://icanhazdadjoke.com', { headers: { 'Accept': 'application/json' }})
.then(function(result){
dispatch(fetchJokeSuccess(result.data))
})
.catch(error => dispatch(fetchJokeFail(error)));
}
}
Redux 减速器
import {combineReducers} from 'redux';
import {FETCH_JOKE, FETCH_JOKE_SUCCESS, FETCH_JOKE_FAILURE} from '../actions';
const defaultStateList = {
isFetching: false,
items:[],
error:{}
};
const joke = (state = defaultStateList, action) => {
switch (action.type){
case FETCH_JOKE:
return {...state, isFetching:true};
case FETCH_JOKE_SUCCESS:
return {...state, isFetching:false, items:action.data};
case FETCH_JOKE_FAILURE:
return {...state, isFetching:false, error:action.data};
default:
return state;
}
};
const rootReducer = combineReducers({
joke
});
export default rootReducer;
笑话组件
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { fetchJokeCall } from '../actions';
class Joke extends Component {
componentDidMount() {
this.timer = setInterval(()=> this.props.fetchJokeCall(), 1000);
}
componentWillUnmount() {
clearInterval(this.timer)
this.timer = null;
}
render() {
return (
<div>
{this.props.joke.joke}
</div>
);
}
}
Joke.propTypes = {
fetchJokeCall: PropTypes.func,
joke: PropTypes.array.isRequired
};
function mapStateToProps(state) {
return {
joke: state.joke.items,
isfetching: state.joke.isFetching
};
}
export default connect(mapStateToProps, { fetchJokeCall })(Joke);
redux-saga
很棒,我一直在使用 redux。它提供了一个很好的 api 来做延迟、轮询、节流、竞争条件、任务取消等事情。所以使用 redux-saga,你可以添加一个 whcih whcih 将继续池化的观察者
function* pollSagaWorker(action) {
while (true) {
try {
const { data } = yield call(() => axios({ url: ENDPOINT }));
yield put(getDataSuccessAction(data));
yield call(delay, 4000);
} catch (err) {
yield put(getDataFailureAction(err));
}
}
}
Redux-Sagas 更好,我们也在我们的应用程序中使用它,这就是您可以使用 Redux-Sagas 进行轮询的方式
只是为了让您了解如何做到这一点,您还需要了解 Redux-Sagas 的工作原理
操作
export const FETCH_JOKE = 'FETCH_JOKE';
export const FETCH_JOKE_SUCCESS = 'FETCH_JOKE_SUCCESS';
export const FETCH_JOKE_FAILURE = 'FETCH_JOKE_FAILURE';
export const START_POLLING = 'START_POLLING';
export const STOP_POLLING = 'STOP_POLLING';
function startPolling() {
return {
type: START_POLLING
};
}
function stopPolling() {
return {
type: STOP_POLLING
};
}
function fetchJoke() {
return {
type: FETCH_JOKE
};
}
function fetchJokeSuccess(data) {
return {
type: FETCH_JOKE_SUCCESS,
data
};
}
function fetchJokeFail(error) {
return {
type: FETCH_JOKE_FAILURE,
error
};
}
减速器
import {combineReducers} from 'redux';
import {FETCH_JOKE, FETCH_JOKE_SUCCESS, FETCH_JOKE_FAILURE, START_POLLING, STOP_POLLING } from '../actions';
const defaultStateList = {
isFetching: false,
items:[],
error:{},
isPolling: false,
};
const joke = (state = defaultStateList, action) => {
switch (action.type){
case FETCH_JOKE:
return {...state, isFetching:true};
case FETCH_JOKE_SUCCESS:
return {...state, isFetching:false, items:action.data};
case FETCH_JOKE_FAILURE:
return {...state, isFetching:false, error:action.data};
case START_POLLING:
return {...state, isPolling: true};
case STOP_POLLING:
return {...state, isPolling: false};
default:
return state;
}
};
const rootReducer = combineReducers({
joke
});
export default rootReducer;
传奇
import { call, put, takeEvery, takeLatest, take, race } from 'redux-saga/effects'
import {FETCH_JOKE, FETCH_JOKE_SUCCESS, FETCH_JOKE_FAILURE, START_POLLING, STOP_POLLING } from '../actions';
import axios from 'axios';
function delay(duration) {
const promise = new Promise(resolve => {
setTimeout(() => resolve(true), duration)
})
return promise
}
function* fetchJokes(action) {
while (true) {
try {
const { data } = yield call(() => axios({ url: ENDPOINT }))
yield put({ type: FETCH_JOKE_SUCCESS, data: data })
yield call(delay, 5000)
} catch (e) {
yield put({ type: FETCH_JOKE_FAILURE, message: e.message })
}
}
}
function* watchPollJokesSaga() {
while (true) {
const data = yield take(START_POLLING)
yield race([call(fetchJokes, data), take(STOP_POLLING)])
}
}
export default function* root() {
yield [watchPollJokesSaga()]
}
您也可以使用 Redux-Observable,如果您想了解更多,请阅读此 article
我一直在处理几乎相同的问题,只是我不关心开始和停止投票。出于某种原因,while 循环一直冻结我的应用程序,所以我放弃了它,而是像这样设置我的传奇。
import { all, takeLatest, call, put } from 'redux-saga/effects';
import axios from 'axios';
import { API_CALL_REQUEST, API_CALL_SUCCESS, API_CALL_FAILURE, API_CALL_FETCHED } from
'../actions/giphy';
function apiFetch() {
let randomWord = require('random-words');
let API_ENDPOINT = `https://api.giphy.com/v1/gifs/search?
api_key=MYKEY&q=${randomWord()}&limit=12`;
return axios({
method: "get",
url: API_ENDPOINT
});
}
export function* fetchImages() {
try {
const res = yield call(apiFetch)
const images = yield res.data
yield put({type: API_CALL_SUCCESS, images})
} catch (e) {
yield put({type: API_CALL_FAILURE, e})
console.log('Error fetching giphy data')
}
}
export default function* giphySaga() {
yield all([
takeLatest(API_CALL_REQUEST, fetchImages),
]);
}
然后在我的组件中添加了这个。
componentDidMount() {
this.interval = setInterval(() => {
this.props.dispatch({type: 'API_CALL_REQUEST'});
}, 5000);
}
componentWillUnmount() {
clearInterval(this.interval)
}
它正在运行,但希望得到一些关于如何改进它的反馈。
这是穷人的方式。我认为这不是最好的方法,但它不需要任何额外的库。
操作
// action types
import axios from 'axios';
export const START_POLLING_JOKE = 'START_POLLING_JOKE';
export const STOP_POLLING_JOKE = 'STOP_POLLING_JOKE';
export const FETCH_JOKE = 'FETCH_JOKE';
export const FETCH_JOKE_SUCCESS = 'FETCH_JOKE_SUCCESS';
export const FETCH_JOKE_FAILURE = 'FETCH_JOKE_FAILURE';
const defaultPollingInterval = 60000
function startPollingJoke(interval = defaultPollingInterval) {
return function (dispatch) {
const fetch = () => dispatch(fetchJoke())
dispatch({
type: START_POLLING_JOKE,
interval,
fetch,
})
}
}
function stopPollingJoke() {
return {
type: STOP_POLLING_JOKE
}
}
function fetchJoke() {
return {
type: FETCH_JOKE
};
}
function fetchJokeSuccess(data) {
return {
type: FETCH_JOKE_SUCCESS,
data
};
}
function fetchJokeFail(error) {
return {
type: FETCH_JOKE_FAILURE,
error
};
}
export function pollJokeCall(interval = defaultPollingInterval) {
return function (dispatch) {
dispatch(fetchJoke())
dispatch(startPollingJoke(interval))
}
}
export function fetchJokeCall() {
return function(dispatch){
dispatch(fetchJoke());
return axios.get('https://icanhazdadjoke.com', { headers: { 'Accept': 'application/json' }})
.then(function(result){
dispatch(fetchJokeSuccess(result.data))
})
.catch(error => dispatch(fetchJokeFail(error)));
}
}
减速器
import {combineReducers} from 'redux';
import {
START_POLLING_JOKE,
STOP_POLLING_JOKE,
FETCH_JOKE,
FETCH_JOKE_SUCCESS,
FETCH_JOKE_FAILURE,
} from '../actions';
const defaultStateList = {
isFetching: false,
items:[],
error:{},
pollingId: null,
polling: false,
};
const joke = (state = defaultStateList, action) => {
switch (action.type){
case START_POLLING_JOKE:
clearInterval(state.pollingId)
return {
...state,
polling: true,
pollingId: setInterval(action.fetch, action.interval),
}
}
case STOP_POLLING_JOKE:
clearInterval(state.pollingId)
return {...state, polling: false, pollingId: null}
case FETCH_JOKE:
return {...state, isFetching:true};
case FETCH_JOKE_SUCCESS:
return {...state, isFetching:false, items:action.data};
case FETCH_JOKE_FAILURE:
return {...state, isFetching:false, error:action.data};
default:
return state;
}
};
const rootReducer = combineReducers({
joke
});
export default rootReducer;
组件(可能有错误,因为我不习惯 class 组件)
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { pollJokeCall, stopPollingJoke } from '../actions';
class Joke extends Component {
componentDidMount() {
this.props.pollJokeCall()
}
componentWillUnmount() {
this.props.stopPollingJoke()
}
render() {
return (
<div>
{this.props.joke.joke}
</div>
);
}
}
Joke.propTypes = {
pollJokeCall: PropTypes.func,
stopPollingJoke: PropTypes.func,
joke: PropTypes.array.isRequired,
};
function mapStateToProps(state) {
return {
joke: state.joke.items,
isfetching: state.joke.isFetching
};
}
export default connect(mapStateToProps, { pollJokeCall, stopPollingJoke })(Joke);
我制作了一个小的(5kb gzipped)助手来创建基于 redux-thunk 存储的轮询。这个想法是有一个逻辑来防止两次注册相同的轮询,在迭代之间有回调等等。