I was testing redux actions using Jest , When i try to test the default action, it throws an Error
Expected value to equal:
{"payload": {"male": "mancha"}, "type": "actions/change_gender"}
Received:
[Function anonymous]
It seems it sends the function, instead of values.
test change_gender.js
import changeGender, { CHANGE_GENDER } from '../change_gender';
const payload = {
type: CHANGE_GENDER,
payload: {
male: 'mancha'
}
};
describe('actions', () => {
it('should Change the ', () => {
const expectedAction = {
type: payload.type,
payload: payload.payload
};
expect(changeGender('male', 'mancha')).toEqual(expectedAction)
});
});
Action change_gender.js
import toggleToolTip from './toggle_tooltip'; // eslint-disable-line
export const CHANGE_GENDER = 'actions/change_gender';
export default(radioType, type) => (dispatch) => {
dispatch({
type: CHANGE_GENDER,
payload: {
[radioType]: type
}
});
};
You should return the dispatch at change_gender.js:
change_gender.js:
import toggleToolTip from './toggle_tooltip'; // eslint-disable-line
export const CHANGE_GENDER = 'actions/change_gender';
export default(radioType, type) => (dispatch) => {
return dispatch({
type: CHANGE_GENDER,
payload: {
[radioType]: type
}
});
};
As Chen-tai mentioned, returning from the dispatch would help here for testing purposes.
The reason you see [Function] being returned is that your action is a function returning a function.
(radioType, type) => (dispatch) => { ... }
The first set of params, followed by the fat arrow is an anonymous function. That then returns another anonymous function that takes a dispatch function as its arguments. So, if we call the action twice, providing a mock dispatch function, we'll get back the expected action!
const action = (radioType, type) => (dispatch) => {
return dispatch({
type: "CHANGE_GENDER",
payload: {
[radioType]: type
}
});
};
console.log(
action('male', 'mancha')((action) => action)
)
We can then write out test:
Action change_gender.js
import toggleToolTip from './toggle_tooltip'; // eslint-disable-line
export const CHANGE_GENDER = 'actions/change_gender';
export default(radioType, type) => (dispatch) => {
return dispatch({
type: CHANGE_GENDER,
payload: {
[radioType]: type
}
});
};
test change_gender.js:
import changeGender, { CHANGE_GENDER } from '../change_gender';
const payload = {
type: CHANGE_GENDER,
payload: {
male: 'mancha'
}
};
describe('actions', () => {
it('should Change the ', () => {
const expectedAction = {
type: payload.type,
payload: payload.payload
};
expect(changeGender('male', 'mancha')((payload) => payload).toEqual(expectedAction)
});
});
Related
import Axios from "axios";
export const ChangeTodoCount = (newCount) => {
return {
type: "CHANGE_TODO_COUNT",
payload: newCount,
};
};
export const FetchToDo = () => {
return (dispatch) => {
Axios.get("http://localhost:2000/todo").then((response) => {
dispatch({
type: "GET_TODO",
payload: response.data,
});
dispatch({
type: "CHANGE_TODO_COUNT",
payload: response.data.length,
});
});
};
};
export const DeleteItem = (id) => {
Axios.delete("http://localhost:2000/todo/" + id).then({ FetchToDo });
};
I'm trying to call FetchToDo after i run DeleteItem.
how would i need to add the FetchToDo to do that?
Right now, when i clicked on the delete button, the list is deleted but i have to refresh the page to re-fetch the list.
`export const DeleteItem = (id) => {Axios.delete("http://localhost:2000/todo/" + id).then(()=>FetchToDo())};`
Hello I'm trying to test this function with the return of the dispatch in how many times it have been called, but really don't know how to do it correctly in order to call dispatch
export const mapDispatchToProps = (dispatch) => {
return {
hideSidebar: () => {
dispatch(hideSidebar)
},
updateUnit: (unitObject) => {
dispatch(settingsActions.updateArray(unitObject))
}
}
}
I have these test
describe('mapDispatchToProps', () => {
test('test', () => {
const dispatch = jest.fn(() => Promise.resolve())
mapDispatchToProps(dispatch)
expect(dispatch).toHaveBeenCalledTimes(2)
})
})
Any suggestions?
Create a dispatch mock function and pass it to mapDispatchToProps.
Then call the functions defined on the result.
You can use something like toHaveBeenCalledWith to verify that the correct action was dispatched:
// Stubs for hideSidebar and settingsActions.updateArray
const hideSidebar = { type: 'hide-side-bar' };
const settingsActions = { updateArray: u => ({ type: 'update-unit', payload: u })};
export const mapDispatchToProps = (dispatch) => {
return {
hideSidebar: () => {
dispatch(hideSidebar)
},
updateUnit: (unitObject) => {
dispatch(settingsActions.updateArray(unitObject))
}
}
}
test('mapDispatchToProps', () => {
const dispatch = jest.fn();
const map = mapDispatchToProps(dispatch);
map.hideSidebar();
expect(dispatch).toHaveBeenCalledWith({ type: 'hide-side-bar' }); // Success!
map.updateUnit({ theKey: 'theVal' });
expect(dispatch).toHaveBeenCalledWith({ type: 'update-unit', payload: { theKey: 'theVal' } }); // Success!
})
I'm using react redux to create an action creator in my app. The point is that when I use async await syntax, it auto returns a promise (without the "return" keyword). However, when I use old-style promise like then(), i have to explicitly type the "return" keyword - otherwise it will return undefined. Why does this happen?
app.js (createStore):
app.get('*', (req, res) => {
const store = createStore(reducers, applyMiddleware(reduxThunk));
const promise = matchRoutes(RouteApp, req.path).map(({ route }) => {
return route.loadData ? route.loadData(store) : null;
});
console.log(promise);
Promise.all(promise).then(() => {
res.send(renderApp(req, store));
});
});
route.js:
export default [
{
loadData,
path: '/',
component: Landing,
exact: true,
},
];
landing.js
function loadData(store) {
return store.dispatch(fetchUser());
}
export { loadData };
When I use async await:
action.js
export const fetchUser = () => async (dispatch) => {
const res = await axios.get('https://react-ssr-api.herokuapp.com/users');
dispatch({
type: INFO_USER,
payload: res.data,
});
};
When I use promise then:
// It doesn't work
export const fetchUser = () => (dispatch) => {
axios.get('https://react-ssr-api.herokuapp.com/users').then((res) => {
dispatch({
type: INFO_USER,
payload: res.data,
});
});
};
"return" keyword
// now it works
export const fetchUser = () => (dispatch) => {
return axios.get('https://react-ssr-api.herokuapp.com/users').then((res) => {
dispatch({
type: INFO_USER,
payload: res.data,
});
});
};
async function always returns a promise, that's its purpose. In case there's no return value, it returns a promise of undefined.
As the reference states,
Return value
A Promise which will be resolved with the value returned by the async
function, or rejected with an uncaught exception thrown from within
the async function.
This async function
export const fetchUser = () => async (dispatch) => {
const res = await axios.get('https://react-ssr-api.herokuapp.com/users');
dispatch({
type: INFO_USER,
payload: res.data,
});
};
is syntactic sugar for this function:
export const fetchUser = () => (dispatch) => {
return axios.get('https://react-ssr-api.herokuapp.com/users').then((res) => {
dispatch({
type: INFO_USER,
payload: res.data,
});
});
};
I am using redux-thunk and want like to dispatch an action and once that is finished make an api call with part of that updated store.
store.js
const middleware = composeEnhancers(applyMiddleware(promise(), thunk, logger()))
const localStore = loadStore()
const store = createStore(reducer, localStore, middleware)
graphActions.js:
First add an Element:
export function addElement(element) {
return dispatch => {
dispatch({
type: ADD_ELEMENT,
payload: element
})
}
}
Then make api call via different action creator:
export function saveElements() {
return (dispatch, getState) => {
let graphId = getState().elements.id
let elements = getState().elements.elements
axios.put(Config.config.url + '/graph/' + graphId, {
'data': JSON.stringify({elements: elements}),
}).then(() => {
dispatch({type: SHOW_SUCCESS_SNACKBAR})
}).catch((err) => {
dispatch({type: SHOW_ERROR_SNACKBAR})
dispatch({type: UPDATE_ELEMENTS_REJECTED, payload: err})
})
}
}
I need to make sure, that addElement() is finished before saveElements(), so that saveElements() accesses the updated store.
I tried the following:
export function addElement(element) {
const promise = (dispatch) => new Promise((resolve) => {
dispatch({
type: ADD_ELEMENT,
payload: element
})
resolve()
})
return dispatch => {
promise(dispatch).then(() => {
saveElements()
})
}
}
ADD_ELEMENT is dispatched, but the actions within saveElements() are not dispatched, no api call is made.
I was missing to dispatch saveElements() and returning dispatch(saveElements()).
export function addElement(element) {
const promise = (dispatch) => new Promise((resolve) => {
dispatch({
type: ADD_ELEMENT,
payload: element
})
resolve()
})
return (dispatch) => {
return addElements(dispatch).then(() => {
return dispatch(saveElements())
})
}
UPDATE:
Noticed I can simply do:
export function addElement(element)
return (dispatch) => {
dispatch({
type: ADD_ELEMENT,
payload: element
})
dispatch(saveElements())
})
}
I have a problem with my epic, please help me find out where I went wrong. Thank you!
The following code leads me to the error:
const loginEpic = (action$) =>
action$
.ofType('LOGIN')
.switchMap(() => {
return Observable.fromPromise(loginService())
.map((result) => {
return Observable.of({
payload: result,
type: types.loginCompleted,
});
})
.catch((error) => {
return Observable.of({
payload: error,
type: types.loginFailed,
});
});
});
And here is my configureStore file:
const epicMiddleware = createEpicMiddleware(rootEpic);
// Ref: https://redux-observable.js.org/docs/recipes/HotModuleReplacement.html
if (module.hot) {
module.hot.accept('./epic', () => {
const nextEpic = require('./epic');
epicMiddleware.replaceEpic(nextEpic);
});
}
const configureStore = (): Store<any> => {
const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(epicMiddleware)));
if (module.hot) {
module.hot.accept('./reducer', () => {
const nextReducer = require('./reducer').default;
store.replaceReducer(nextReducer);
});
return store;
}
};
I believe you must drop the Observable.of(...) from within the map() (EDIT - thanks to paulpdaniels: but not the catch()) method, because in this way you are returning an observable of observables - see the simplified code below:
Observable.fromPromise(...)
.map(result => Observable.of(...)) // maps an object to an observable
The entire code should be:
const loginEpic = (action$) =>
action$
.ofType('LOGIN')
.switchMap(() => {
return Observable.fromPromise(loginService())
.map((result) => {
return {
payload: result,
type: types.loginCompleted,
};
})
.catch((error) => {
return Observable.of({
payload: error,
type: types.loginFailed,
});
});
});