У меня это работает, но мне нужен более «лучший способ».
он использует API https: // icanhazdadjoke для отображения случайной шутки, которая обновляется каждые 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)));
}
}
Редукс редуктор
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 thunk. Я бы посоветовал вам ознакомиться с нашей книгой Nir Kaufmans о написании собственного промежуточного программного обеспечения. Все эти побочные эффекты отлично подходят для промежуточного программного обеспечения.





redux-saga великолепен, и я использовал его с redux. Он предоставляет отличный API для таких вещей, как задержка, опрос, регулирование, условия гонки, отмена задач. Итак, используя redux-saga, вы можете добавить наблюдателя, который будет продолжать объединять
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));
}
}
}Нужен ли этот шлейф while (true)? Похоже, я хочу избежать этого.
Спасибо. На своем примере я использовал другой API, заполняющий таблицу. С помощью redux-saga это предотвратит обновление всей таблицы каждый раз? также как насчет использования веб-сокетов? это хорошо для производительности?
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, если хотите больше узнать об этом, прочтите этот статья
Вы можете показать мне скрипку из этой работы? возможно со случайной шуткой api (icanhazdadjoke.com/api)
Обновил ответ
@Adam Вы реализовали это, я думаю, это ответ, поскольку вы хотели реализовать опрос с использованием redux?
payloadIfAny не определено, dataIfAny не определено. также в часах вы определяете данные, но никогда их не используете.
это идея того, как вы можете опросить, используя саги о сокращении, я знаю, что этот фрагмент кода может не работать прямо сейчас. Прочтите это, это поможет вам во многом bigbitecreative.com/polling-with-redux
WatchPollJokesSaga также никогда не вызывается. я не думаю, что это ответ, так как неясно, что он делает
Также задержка не работает. если вы не измените порядок, который затем оставляет огромную задержку при первом вызове yield yield (delay, 5000) yield put ({type: FETCH_JOKE_SUCCESS, data: data})
Я думаю, вам нужно немного изучить redux-saga, потому что это может быть немного сложно понять. Кстати, вам нужно создать createSagaMiddleware () и createStore (), а затем запустить промежуточное программное обеспечение саги.
Это сложно, поэтому я попросил в качестве ответа рабочую скрипку. то, что вы предоставили, не работает и делает еще более сложную тему. Можете ли вы предоставить рабочую скрипку?
Обновил ответ, надеюсь, это поможет.
Спасибо, сегодня я сделал нечто очень похожее. Я могу подтвердить, что ваш пример работает, и он поможет другим. Но теперь он работает, и я думаю, что веб-сокеты могут быть более современным решением. мысли?
Да, вы правы, я тоже использовал веб-сокеты, и вам не нужен опрос, потому что вы можете реализовать прослушиватели и функции обратного вызова, но я думаю, что использование веб-сокетов только для получения данных API - это не то, что я хотел бы делать. Я бы использовал веб-сокеты для таких вещей, как чат или видеозвонок.
Да, согласен, но я работаю над приложением, в котором действие пользователя с записью помещает ее в процесс, в котором статус этой записи изменится с, например, неполный, выполняется, завершенный. просто подумал, что это может быть лучше подходит для веб-сокетов, так как у меня будет контроль над серверной частью в узле для отправки обновленных записей. ты согласен?
Согласен. если вы хотите избавиться от бесполезного сетевого потребления и работать в режиме реального времени.
Я работал почти над той же проблемой, за исключением того, что меня не беспокоили запуск и остановка опроса. По какой-то причине цикл 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.info('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;
Компонент (может быть ошибка, потому что я не привык к компонентам классов)
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 gzip) помощник для создания опроса на основе хранилища redux-thunk. Идея состоит в том, чтобы иметь логику, предотвращающую регистрацию одного и того же опроса дважды, иметь обратные вызовы между итерациями и многое другое.
Вы можете попробовать промежуточное программное обеспечение Thunk, вы отправляете результаты выборки, используя свои действия, которые применяются к вашему состоянию. Это очень маленькая библиотека, и она используется довольно часто.