Skip to content

Instantly share code, notes, and snippets.

@james-gardner
Created March 17, 2020 16:40
Show Gist options
  • Save james-gardner/45253f74d01a7031208ad462d4849bb5 to your computer and use it in GitHub Desktop.
Save james-gardner/45253f74d01a7031208ad462d4849bb5 to your computer and use it in GitHub Desktop.
useReducer vs Custom Hook
import React, { createContext, useReducer, useContext } from 'react'
import PropTypes from 'prop-types'
const ServiceRequestContext = createContext()
export const initialState = {
serviceRequests: new Map(),
selectedTicketId: null,
pendingRequest: null
}
/**
* Action types.
*/
export const SELECT_SERVICE_REQUEST = 'SELECT_SERVICE_REQUEST'
export const CREATE_SERVICE_REQUEST = 'CREATE_SERVICE_REQUEST'
export const LOAD_SERVICE_REQUEST = 'LOAD_SERVICE_REQUEST'
export const SAVE_SERVICE_REQUEST = 'SAVE_SERVICE_REQUEST'
export const LOAD_PENDING = 'LOAD_PENDING'
export const LOAD_SUCCESS = 'LOAD_SUCCESS'
export const LOAD_FAILURE = 'LOAD_FAILURE'
/**
* Action creators.
*/
export const selectServiceRequest = ticketId => ({
type: SELECT_SERVICE_REQUEST,
payload: {
ticketId
}
})
export const createServiceRequest = payload => ({
type: CREATE_SERVICE_REQUEST,
payload
})
export const loadServiceRequest = payload => ({
type: LOAD_SERVICE_REQUEST,
payload: {
ticketId
}
})
export const saveServiceRequest = payload => ({
type: SAVE_SERVICE_REQUEST,
payload
})
/**
* Selectors.
*/
export const getSelectedServiceRequest = state =>
state.serviceRequests.get(state.selectedTicketId)
/**
* Reducer. Used for complex state transitions.
*/
export const reducer = (state, action) => {
switch(action.type) {
case LOAD_PENDING:
return {
...state,
pendingRequest: action.payload.pendingRequest
}
case LOAD_FAILURE:
case LOAD_SUCCESS:
return {
...state,
pendingRequest: null
}
case SELECT_SERVICE_REQUEST:
return {
...state,
serviceRequests: state.serviceRequests.set(action.payload.ticketId, {
data: undefined
}),
selectedTicketId: action.payload.ticketId
}
default:
throw new Error('unknown action type');
}
}
/**
* Async middleware.
*
* This middleware wraps the dispatch method to pick up on actions that have async dependencies
* such as data for different parts of the form.
*
* See: https://gist.github.com/astoilkov/013c513e33fe95fa8846348038d8fe42
*/
const middleware = dispatch =>
action => {
switch (action.type) {
case LOAD_SERVICE_REQUEST:
dispatch({
type: LOAD_PENDING,
payload: {
pendingRequest: null
}
})
return
// Pass through to dispatch.
default:
return dispatch(action);
}
}
/**
* Provides a context which houses all selected SRs e.g. search selections.
*/
export const ServiceRequestProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<ServiceRequestContext.Provider value={[state, middleware(dispatch)]}>
{children}
</ServiceRequestContext.Provider>
)
}
ServiceRequestProvider.propTypes = {
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired
}
export const useServiceRequests = () => useContext(ServiceRequestContext)
import { useEffect, useState, useRef } from 'react'
import axios from 'axios'
export const useObject = (api, objectType, query) => {
const [ state, setState ] = useState({
loading: true,
data: undefined,
error: undefined
})
const ref = useRef()
const loadObject = async (objectType, query) => {
ref.current = axios.CancelToken.source()
setState({ data: undefined, error: undefined, loading: true })
try {
const res = await api.get(`/os/${objectType}?${query}`, {
cancelToken: ref.current.token,
useCache: true
})
setState({ data: res.data, loading: false })
} catch (err) {
if (!axios.isCancel(err)) {
setState(state => ({
...state,
loading: false,
error: true
}))
}
}
}
useEffect(() => {
loadObject(objectType, query)
return () => {
if (ref.current) {
ref.current.cancel()
}
}
}, [query])
return state
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment