I've got this working but i'm after a more 'best practice way'.
its using the https://icanhazdadjoke api to display a random joke that gets updated every x seconds. is there a better way of doing this?
eventually i want to add stop, start, reset functionality and feel this way might not be the best.
Any middleware i can use?
Redux actions
// 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 reducer
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;
Joke component
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-Sagas is better and we are using it in our applications as well, this is how you can poll using Redux-Sagas
Just to give you an idea this is how you can do it, You also need to understand how Redux-Sagas work
Action
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
};
}
Reducer
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;
Sagas
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()]
}
You can also use Redux-Observable, if you want to get more into this read this article
I have made a small (5kb gzipped) helper to create polling based on redux-thunk store. The idea is to have a logic to prevent registering the same polling twice, have callbacks between iterations and more.
https://www.npmjs.com/package/redux-polling-thunk
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With