Created
May 17, 2021 10:49
-
-
Save TCotton/3c0f5b549bd919aa34d9274053c1cf3b to your computer and use it in GitHub Desktop.
Redux saga example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// customer | |
import { put, call } from 'redux-saga/effects'; | |
const fetch = (url, data) => | |
window.fetch(url, { | |
body: JSON.stringify(data), | |
method: 'POST', | |
credentials: 'same-origin', | |
headers: { 'Content-Type': 'application/json' } | |
}); | |
export function* addCustomer({ customer }) { | |
yield put({ type: 'ADD_CUSTOMER_SUBMITTING' }); | |
const result = yield call(fetch, '/customers', customer); | |
if (result.ok) { | |
const customerWithId = yield call([result, 'json']); | |
yield put({ | |
type: 'ADD_CUSTOMER_SUCCESSFUL', | |
customer: customerWithId | |
}); | |
} else if (result.status === 422) { | |
const response = yield call([result, 'json']); | |
yield put({ | |
type: 'ADD_CUSTOMER_VALIDATION_FAILED', | |
validationErrors: response.errors | |
}); | |
} else { | |
yield put({ type: 'ADD_CUSTOMER_FAILED' }); | |
} | |
} | |
const defaultState = { | |
customer: {}, | |
status: undefined, | |
validationErrors: {}, | |
error: false | |
}; | |
export const reducer = (state = defaultState, action) => { | |
switch (action.type) { | |
case 'ADD_CUSTOMER_SUBMITTING': | |
return { status: 'SUBMITTING' }; | |
default: | |
return state; | |
} | |
}; | |
// store | |
import { put, call } from 'redux-saga/effects'; | |
const fetch = (url, data) => | |
window.fetch(url, { | |
body: JSON.stringify(data), | |
method: 'POST', | |
credentials: 'same-origin', | |
headers: { 'Content-Type': 'application/json' } | |
}); | |
export function* addCustomer({ customer }) { | |
yield put({ type: 'ADD_CUSTOMER_SUBMITTING' }); | |
const result = yield call(fetch, '/customers', customer); | |
if (result.ok) { | |
const customerWithId = yield call([result, 'json']); | |
yield put({ | |
type: 'ADD_CUSTOMER_SUCCESSFUL', | |
customer: customerWithId | |
}); | |
} else if (result.status === 422) { | |
const response = yield call([result, 'json']); | |
yield put({ | |
type: 'ADD_CUSTOMER_VALIDATION_FAILED', | |
validationErrors: response.errors | |
}); | |
} else { | |
yield put({ type: 'ADD_CUSTOMER_FAILED' }); | |
} | |
} | |
const defaultState = { | |
customer: {}, | |
status: undefined, | |
validationErrors: {}, | |
error: false | |
}; | |
export const reducer = (state = defaultState, action) => { | |
switch (action.type) { | |
case 'ADD_CUSTOMER_SUBMITTING': | |
return { status: 'SUBMITTING' }; | |
default: | |
return state; | |
} | |
}; | |
// customer test | |
import { storeSpy, expectRedux } from 'expect-redux'; | |
import 'whatwg-fetch'; | |
import { | |
fetchResponseOk, | |
fetchResponseError | |
} from '../spyHelpers'; | |
import { configureStore } from '../../src/store'; | |
import { reducer } from '../../src/sagas/customer'; | |
describe('addCustomer', () => { | |
const customer = { id: 123 }; | |
let store; | |
beforeEach(() => { | |
jest | |
.spyOn(window, 'fetch') | |
.mockReturnValue(fetchResponseOk(customer)); | |
store = configureStore([storeSpy]); | |
}); | |
const dispatchRequest = customer => | |
store.dispatch({ | |
type: 'ADD_CUSTOMER_REQUEST', | |
customer: customer | |
}); | |
it('sets current status to submitting', () => { | |
dispatchRequest(); | |
return expectRedux(store) | |
.toDispatchAnAction() | |
.matching({ type: 'ADD_CUSTOMER_SUBMITTING' }); | |
}); | |
it('submits request to the fetch api', async () => { | |
const inputCustomer = { firstName: 'Ashley' }; | |
dispatchRequest(inputCustomer); | |
expect(window.fetch).toHaveBeenCalledWith('/customers', { | |
body: JSON.stringify(inputCustomer), | |
method: 'POST', | |
credentials: 'same-origin', | |
headers: { 'Content-Type': 'application/json' } | |
}); | |
}); | |
it('dispatches ADD_CUSTOMER_SUCCESSFUL on success', () => { | |
dispatchRequest(); | |
return expectRedux(store) | |
.toDispatchAnAction() | |
.matching({ type: 'ADD_CUSTOMER_SUCCESSFUL', customer }); | |
}); | |
it('dispatches ADD_CUSTOMER_FAILED on non-specific error', () => { | |
window.fetch.mockReturnValue(fetchResponseError()); | |
dispatchRequest(); | |
return expectRedux(store) | |
.toDispatchAnAction() | |
.matching({ type: 'ADD_CUSTOMER_FAILED' }); | |
}); | |
it('dispatches ADD_CUSTOMER_VALIDATION_FAILED if validation errors were returned', () => { | |
const errors = { field: 'field', description: 'error text' }; | |
window.fetch.mockReturnValue( | |
fetchResponseError(422, { errors }) | |
); | |
dispatchRequest(); | |
return expectRedux(store) | |
.toDispatchAnAction() | |
.matching({ | |
type: 'ADD_CUSTOMER_VALIDATION_FAILED', | |
validationErrors: errors | |
}); | |
}); | |
}); | |
describe('reducer', () => { | |
it('returns a default state for an undefined existing state', () => { | |
expect(reducer(undefined, {})).toEqual({ | |
customer: {}, | |
status: undefined, | |
validationErrors: {}, | |
error: false | |
}); | |
}); | |
describe('ADD_CUSTOMER_SUBMITTING action', () => { | |
const action = { type: 'ADD_CUSTOMER_SUBMITTING' }; | |
it('sets status to SUBMITTING', () => { | |
expect(reducer(undefined, action)).toMatchObject({ | |
status: 'SUBMITTING' | |
}); | |
}); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment