React redux api polling каждые x секунд

У меня это работает, но мне нужен более «лучший способ».

он использует 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);

Вы можете попробовать промежуточное программное обеспечение Thunk, вы отправляете результаты выборки, используя свои действия, которые применяются к вашему состоянию. Это очень маленькая библиотека, и она используется довольно часто.

Xorifelse 19.09.2018 22:45

Похоже, он уже использует redux thunk. Я бы посоветовал вам ознакомиться с нашей книгой Nir Kaufmans о написании собственного промежуточного программного обеспечения. Все эти побочные эффекты отлично подходят для промежуточного программного обеспечения.

mattdevio 20.09.2018 00:13
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
13
2
13 101
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

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)? Похоже, я хочу избежать этого.

Xorifelse 19.09.2018 22:51

Спасибо. На своем примере я использовал другой API, заполняющий таблицу. С помощью redux-saga это предотвратит обновление всей таблицы каждый раз? также как насчет использования веб-сокетов? это хорошо для производительности?

Adam 20.09.2018 00:20
Ответ принят как подходящий

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 20.09.2018 22:10

Обновил ответ

Haider Ali 20.09.2018 23:07

@Adam Вы реализовали это, я думаю, это ответ, поскольку вы хотели реализовать опрос с использованием redux?

Haider Ali 22.09.2018 01:01

payloadIfAny не определено, dataIfAny не определено. также в часах вы определяете данные, но никогда их не используете.

Adam 28.09.2018 21:28

это идея того, как вы можете опросить, используя саги о сокращении, я знаю, что этот фрагмент кода может не работать прямо сейчас. Прочтите это, это поможет вам во многом bigbitecreative.com/polling-with-redux

Haider Ali 28.09.2018 21:33

WatchPollJokesSaga также никогда не вызывается. я не думаю, что это ответ, так как неясно, что он делает

Adam 28.09.2018 21:40

Также задержка не работает. если вы не измените порядок, который затем оставляет огромную задержку при первом вызове yield yield (delay, 5000) yield put ({type: FETCH_JOKE_SUCCESS, data: data})

Adam 28.09.2018 22:17

Я думаю, вам нужно немного изучить redux-saga, потому что это может быть немного сложно понять. Кстати, вам нужно создать createSagaMiddleware () и createStore (), а затем запустить промежуточное программное обеспечение саги.

Haider Ali 28.09.2018 22:20

Это сложно, поэтому я попросил в качестве ответа рабочую скрипку. то, что вы предоставили, не работает и делает еще более сложную тему. Можете ли вы предоставить рабочую скрипку?

Adam 29.09.2018 17:40

Обновил ответ, надеюсь, это поможет.

Haider Ali 29.09.2018 18:12

Спасибо, сегодня я сделал нечто очень похожее. Я могу подтвердить, что ваш пример работает, и он поможет другим. Но теперь он работает, и я думаю, что веб-сокеты могут быть более современным решением. мысли?

Adam 30.09.2018 20:03

Да, вы правы, я тоже использовал веб-сокеты, и вам не нужен опрос, потому что вы можете реализовать прослушиватели и функции обратного вызова, но я думаю, что использование веб-сокетов только для получения данных API - это не то, что я хотел бы делать. Я бы использовал веб-сокеты для таких вещей, как чат или видеозвонок.

Haider Ali 30.09.2018 20:07

Да, согласен, но я работаю над приложением, в котором действие пользователя с записью помещает ее в процесс, в котором статус этой записи изменится с, например, неполный, выполняется, завершенный. просто подумал, что это может быть лучше подходит для веб-сокетов, так как у меня будет контроль над серверной частью в узле для отправки обновленных записей. ты согласен?

Adam 30.09.2018 20:29

Согласен. если вы хотите избавиться от бесполезного сетевого потребления и работать в режиме реального времени.

Haider Ali 30.09.2018 20:32

Я работал почти над той же проблемой, за исключением того, что меня не беспокоили запуск и остановка опроса. По какой-то причине цикл 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. Идея состоит в том, чтобы иметь логику, предотвращающую регистрацию одного и того же опроса дважды, иметь обратные вызовы между итерациями и многое другое.

https://www.npmjs.com/package/redux-polling-thunk

Другие вопросы по теме