I am struggling to unit test the store in the componentWillLoad() lifecycle.
I have tried following this guide: https://github.com/dmitry-zaets/redux-mock-store
component.tsx
#Prop({ context: "store" }) store: Store;
componentWillLoad() {
this.store.mapDispatchToProps(this, { updateStep });
}
spec.ts
const middlewares = [thunk];
const mockStore = configureStore(middlewares);
const mapDispatchToProps = () => ({ type: updateStep});
it('should test componentWillLoad', () => {
const initialState = {};
const store = mockStore(initialState);
store.dispatch(mapDispatchToProps());
const actions = store.updateStep();
const expectedPayload = { type: updateStep};
expect(actions).toEqual([expectedPayload]);
});
I get the following error atm: TypeError: mockStore is not a function
Related
I am trying to display film details from this API https://swapi.dev/api/films in NextJS but I keep getting an error. It has something to do with the getStaticPath function. I am using this code: any help welcome.
import fetch from 'isomorphic-unfetch';
export const getStaticPaths = async () => {
const res = await fetch('https://swapi.dev/api/films');
const data = await res.json();
console.log("This is " + JSON.stringify(data));
const paths = data.results.map(film => {
return {
params: { id: film.episode_id.toString() },
};
});
return {
paths,
fallback: false,
};
};
export const getStaticProps = async (context) => {
const id = context.params.id;
const res = await fetch(`https://swapi.dev/api/films` + id);
const data = await res.json();
return {
props: { film: data },
};
};
const Details = ({ film }) => {
return (
<div>
<h1>Episode {film.episode_id}</h1>
<h1>{film.title}</h1>
</div>
);
};
export default Details;
I am expecting the episode ID and title to display
enter image description here
I have tried taking 'results' out of 'data.results.map', so 'data.map' but just results in a error says data.map is not a function...I guess because data is an object not an array. But results is an array so I am still lost. I do think the issue lies here somewhere though...
i have component use GetStaticProps like this
export const getStaticProps: GetStaticProps = async () => {
const landingPage = await axios.get(
'https://someapi/'
);
const gameData = await axios.get(
'https://someapi'
);
return {
props: {
landingPageData: landingPage.data,
gameData: gameData.data,
},
revalidate: 120,
};
};
I try to do like this
const promise1 = axios.get(URL1);
const promise2 = axios.get(URL2);
const promise3 = axios.get(URL3);
Promise.all([promise1, promise2, promise3]).then(function(data) {
console.log(data);
});
But I don't know how send the data into the return props
because of the data inside Promise.All
i want to moving to #reduxjs/toolkit, actually my app use the following packages:
"#reduxjs/toolkit": "^1.8.3",
"react-redux": "^7.2.2",
"#types/react-redux": "^7.1.20",
actually i use this type of action for my redux store :
export function action<T extends string, P>(type: T, payload?: Partial<P>) {
return { type, ...payload };
}
i change my configure store like this :
const sagaMiddleware = createSagaMiddleware();
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) => [...getDefaultMiddleware(), sagaMiddleware],
});
sagaMiddleware.run(rootSaga);
export default store;
type AppDispatch = typeof store.dispatch;
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
i've created a simple button component like this :
interface FavoriteBtnProps {
className: string;
}
function FavoriteBtn({ className }: FavoriteBtnProps) {
const { windowDisplay } = useAppSelector(getFavoritePlacesState);
const dispatch = useDispatch();
const handleClick = () => {
dispatch(setWindowDisplay(!windowDisplay));
};
return (
<div className={className} onClick={handleClick}>
<FontAwesomeIcon icon={faBookmark} />
</div>
);
}
export default FavoriteBtn;
it work's fine but need to use generic redux dispatch (Dispatch()). the useAppDispatch created don't work with old actions
now i want to add in a parent component of FavoriteBtn
const { leftPanelDisplay } = useAppSelector(getPanelState);
const { windowDisplay } = useAppSelector(getFavoritePlacesState);
console.log(windowDisplay);
const dispatch = useDispatch();
const handleCollapseIcon = () => {
dispatch(
leftPanelDisplay
? panelsActions.leftPanel.collapse()
: panelsActions.leftPanel.uncollapse()
);
// dispatch(setWindowDisplay(!windowDisplay));
};
this code works fine the console log give me the good value, but if i uncomment
dispatch(setWindowDisplay(!windowDisplay));
i have this error :
Uncaught TypeError: Cannot destructure property 'windowDisplay' of
'Object(...)(...)' as it is undefined.
and i don't see favoriteAndHistoric state in the reduxDevTools
only the state created by the toolkit crashes.
here my slice
const favoritePlacesSlice = createSlice({
name: 'favoritePlaces',
initialState,
reducers: {
setWindowDisplay: (state, action: PayloadAction<boolean>) => {
state.windowDisplay = action.payload;
},
setFavorites: (state, action: PayloadAction<FavoriteSearch[]>) => {
state.favoritePlaces = action.payload;
},
},
extraReducers: (builder) => {
// GET ALL
builder.addCase(fetchFavoriteSearchs.pending, (state, action) => {
state.apiStatus = APIStatus.PENDING;
});
builder.addCase(fetchFavoriteSearchs.fulfilled, (state, action) => {
state.apiStatus = APIStatus.FULFILLED;
state.favoritePlaces = action.payload ?? [];
});
// ADD
builder.addCase(addFavoriteSearch.pending, (state, action) => {
state.apiStatus = APIStatus.PENDING;
});
builder.addCase(addFavoriteSearch.fulfilled, (state, action) => {
state.apiStatus = APIStatus.FULFILLED;
state.favoritePlaces = state.favoritePlaces.concat(action.payload);
});
// DELETE
builder.addCase(deleteFavoriteSearch.pending, (state, action) => {
state.apiStatus = APIStatus.PENDING;
});
builder.addCase(deleteFavoriteSearch.fulfilled, (state, action) => {
state.apiStatus = APIStatus.FULFILLED;
state.favoritePlaces = state.favoritePlaces.filter(
(f) => f.idIri !== action.payload
);
});
},
});
export const { setFavorites, setWindowDisplay } = favoritePlacesSlice.actions;
export default favoritePlacesSlice.reducer;
my rootReducer :
import { combineReducers } from 'redux';
...
import favoritePlacesReducer from '../features/favoritePlaces/favoritePlacesSlice';
const rootReducer = combineReducers({
...
favoritePlacesReducer,
});
export default rootReducer;
export type RootState = ReturnType<typeof rootReducer>;
i've allready tryed to reinstall #reduxjs/toolkit and react-redux from scratch but don't fix the problem and more, i have type errors on createAsyncThunk.
why it works on a componant and not on an other?
thx for help
I've been following the process for making an API call and storing it in global state with Redux using this project that I got from a Medium article. So far everything seems to work alright, no errors, but when I go to retrieve the global state there is nothing there. It doesn't seem to have been updated by the action that makes the API call. The relevant bits of code are as follows:
in reducers.js:
const initialState = {
mods: [],
pagination: { pageSize: 15, numPages: 1 },
sortFilter: "mostPopular",
};
const globalState = (state = initialState, action) => {
switch (action.type) {
case SET_MOD_LIST:
return { ...state, mods: state.mods };
case SET_MOD_DETAILS:
return { ...state };
default:
return state;
}
};
const rootReducer = combineReducers({
globalState,
});
export default rootReducer;
in actions.js:
export const fetchModList = (pagination, sortFilter = "mostPopular") => {
const { pageSize = 15, numPages = 1 } = pagination ?? {};
return async (dispatch) => {
const response = await fetch(
`https://www.myapi.com/mods?page=${numPages}&pageSize=${pageSize}&sortBy=${sortFilter}`
);
const resData = await response.json();
dispatch({ type: SET_MOD_LIST, mods: resData });
};
};
in index.js (Next.js root page):
const mods = useSelector((state) => state);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchModList({pageSize:2}));
}, [dispatch]);
console.log({mods})
This is 100% a result of Redux ignorance, this is my first project using it which I'm doing for an interview. Any help would be much appreciated!
Looks like you're setting mods to its own value mods: state.mods. Did you mean to set a value from action.payload rather than state.mods?
I am learning how to unit test a simple action creator as seen below and want to find out the best way to test it. I've been going off an example from the redux docs on writing tests but wonder if it is possible to test async actions with lambda chaining.
Action:
export const toggleSelect = (id, key) => dispatch => {
return dispatch({
type: TOGGLE_LIST_ITEM,
payload: { id, key },
});
};
Test (jest)
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import * as actions from '../';
import * as types from '../types';
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe('list actions', () => {
it('should create an action to unselect all list items', () => {
const id = '123';
const key = 'selectedProspects';
const expectedAction = {
type: types.UNSELECT_ALL_OF_TYPE,
key,
};
const store = mockStore();
return actions.toggleSelect(id, key).then(() => {
expect(store.getActions()).toEqual(expectedAction);
});
});
});
does anyone know of a good way to test this? I am not sure if this not working being indicative to writing more testable code, or if I am just missing something.
You are very close, you just need to make use of dispatch:
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe('list actions', () => {
it('should create an action to unselect all list items', () => {
const id = '123';
const key = 'selectedProspects';
const expectedAction = {
type: types.UNSELECT_ALL_OF_TYPE,
key,
};
const store = mockStore();
return store.dispatch(actions.toggleSelect(id, key)).then(() => {
expect(store.getActions()).toEqual(expectedAction);
});
});
});
Your action itself doesn't need to be a thunk. You could easily write that as an action creator... i.e.
export function toggleSelect(id, key) {
return {
type: TOGGLE_LIST_ITEM,
payload: { id, key },
};
}