Autorefresh function passing id from Url - reactjs

I want to have autorefresh function while passing id from the URL
componentDidMount() {
this.interval = setInterval( this.props.fetchId({ id: this.props.match.params.id }), 15000)
this.props.fetchId({ id: this.props.match.params.id })
}
Implementation of fetchId in actions/index.js
export const FETCH_ID = 'fetch_id'
export const fetchId = (params) => async (dispatch, getState, api) => {
const res = await api.get('/api/records/' + params.id)
dispatch({
type: FETCH_ID,
payload: { id: params.id, data: res.data }
})
}
I tried with above code ,it works fine with initial load but after the component is rendered ,this.props.match.params.id is getting undefined and getting "Uncaught SyntaxError: Unexpected identifier"

I found answer for this question,I was invoking function instead of calling it.When I called the function like below, auto refresh started working as expected
componentDidMount() {
this.interval = setInterval( () => this.props.fetchId({ id: this.props.match.params.id }), 15000)
this.props.fetchId({ id: this.props.match.params.id })
}

Related

Redux async call with then does not wait for api response

I'm defining a Redux service to call an api endpoint:
export const TrackersApi = {
getBasicsTrackers: async (): Promise<ReturnType<typeof recreator>> => {
const url = "/api/getbasictrackers"
const {data, statusText} = await axios.get(url, { withCredentials: true });
if(statusText !== 'OK' && statusText !== 'No Content') throw new Error('Wrong response from getbasictrackers')
const result = recreator(data)
console.log({result})
return result
},
The log returns the json response.
Then I inject this in a component on mount:
componentDidMount = () => {
store.dispatch(getBasicTrackers()).then(() => {
if(this.props.trackers) {
this.setState({
sortedAndFilteredTrackers : this.props.trackers
})
}
if(this.props.folders) {
this.setState({
sortedAndFilteredFolders: this.props.folders
})
}
console.log('trackers', this.props.trackers)
})
}
However the log here returns an empty array. I tried first without the then and I had the same issue.
How can I make it so that the setState is called only once the API response is received?
Additional info: This props is then used to fill in a table. However the table remains empty, which is the key issue here
It is mapped through this:
const mapStateToProps = (state: ReduxStore.State) => ({
trackers: state.trackersData.rawTrackers ? Object.values(state.trackersData.rawTrackers).map(item => ({...item, checked: false})) : [],
folders: state.trackersData?.folders ? Object.values(state.trackersData.folders).map((folder: any) => ({...folder.summary, checked: false})) : []
})

mocking my fetch function does not work, keeps getting back undefined

I am trying to mock a simple function that uses fetch. The function in question looks like this:
export const getPokemon = async () => {
//function that makes the API call and fetches our pokemon
//getPokemon.js
const randomId = () => Math.floor(Math.random() * 151 + 1);
const pokemonApiUrl = `https://pokeapi.co/api/v2/pokemon/`;
export const getPokemon = async () => {
//function that makes the API call and fetches our pokemon
const id = randomId();
let pokemon = { name: "", image: "" };
try {
const result = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`);
console.log(result)
const data = await result.json();
pokemon.name = data.name;
pokemon.image = data.sprites.other["official-artwork"].front_default;
return pokemon;
} catch (err) {
console.error(err);
Whenever I try to mock the function in my unit tests I receive back a TypeError: Cannot read property 'json' of undefined. Basically, the result comes back as undefined and thus we cannot call our .json(). It works fine in production and the fetch calls work as expected. I am using React Testing Library and Jest.
I have tried to replaced the global fetch in the following manner:
//PokemonPage.test.js
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ data: { name: 'Charizard' } }),
})
);
I've also tried to create a fakeFetch and send it in to my function as a dependency injection, but I get the exact same error.
Lastly, I've tried to install jest-fetch-mock but yet again I am getting the same error... Has anyone encountered the same thing?
The failing function gets called in production here:
function Pokemon({ pokemonTrainer }) {
...
useEffect(() => {
async function fetchData() {
pokemonRef.current = await getPokemon();
setPokemonList((prev) => [
...prev,
{ name: pokemonRef.current.name, image: pokemonRef.current.image },
]);
}
fetchData();
}, []);
...
}

Redux Action return undefined

So, Iā€™m building an Expense App with React Native and Redux and I have this two actions:
export const getExpenses = () => async (dispatch) => {
await db.onSnapshot((querySnapshot) => {
const data = [];
querySnapshot.forEach((doc) => {
const { description, name, value, date, type } = doc.data();
data.push({
key: doc.id,
doc, // DocumentSnapshot
description,
date,
name,
value,
type,
});
});
dispatch({
type: TYPES.GET_EXPENSES,
payload: data,
});
dispatch({
type: TYPES.SET_LOADING_EXPENSES,
payload: false,
});
console.log('getExpenses', data);
});
};
export const filterMonthInfo = () => async (dispatch, getState) => {
const info = getState().monthExpenses.data; // data returned by getExpenses()
const currentMonth = getState().dateReducer.currentDate;
const filteredInfo = info
.filter(
(data) => moment(moment(data.date).format('DD/MM/YYYY')).format('MMMM YYYY') === currentMonth,
)
.sort((a, b) => new Date(b.date) - new Date(a.date));
dispatch({
type: TYPES.GET_FILTERED_EXPENSES,
payload: filteredInfo,
});
console.log('filtermonth', filteredInfo);
};
In the Screen where I want to use the data returned by filterMonthInfo i have the following useEffect:
useEffect(() => {
getExpenses();
filterMonthInfo();
getCurrentDate();
}, []);
But since getExpenses is an async function, filterMonthInfo will run first and is going to return undefined because this last function is filtered based on data returned by getExpenses.
What is the best approach so I can make getExpenses run first and then filterMonthInfo?
Thank you
If you want to run a code after an async call is finished, you have to wait for it using Promise. write the code as
useEffect(() => {
getExpenses()
.then(()=>{
filterMonthInfo();
getCurrentDate();
}
);
}, []);
or use async await as it makes syntax more clear

react Invalid attempt to spread non-iterable instance

I keep getting this error:
TypeError: Invalid attempt to spread non-iterable instance
while I'm trying to fetch some data in here :
export const genres = () => {
const apiUrl = "http://localhost:3000/api";
return fetch(apiUrl + "/genres")
.then(response => response.json())
.then(data => {
const res = Array.from(data.results);
return res;
});
};
console.log(genres)
export function getGenres() {
return genres().then(res => res.filter(g => g));
}
and updating the state of my component in here :
componentDidMount() {
const genres = [{ _id: "", name: "All Genres" }, ...getGenres()];
this.setState({ genres});
}
I'm aware that probleme comes from the fact that genres returns an object while the state should be an array but I'm not sure how to fixe it.
Thanks
getGenres returns a promise, so you need to wait for it to resolve before you try to put what is returned from it in state.
componentDidMount() {
getGenres().then(res => {
const genres = [{ _id: "", name: "All Genres" }, ...res];
this.setState({ genres });
})
}

Testing Observable epic which invokes other epic

I am trying to test a Redux Observable epic which dispatches an action to invoke an other epic. Somehow the invoked epic is not called.
Lets say my epics looks like this;
const getJwtEpic = (action$, store, { api }) =>
action$.ofType('GET_JWT_REQUEST')
.switchMap(() => api.getJWT()
.map(response => {
if (response.errorCode > 0) {
return {
type: 'GET_JWT_FAILURE',
error: { code: response.errorCode, message: response.errorMessage },
};
}
return {
type: 'GET_JWT_SUCCESS',
idToken: response.id_token,
};
})
);
const bootstrapEpic = (action$, store, { api }) =>
action$.ofType('BOOTSTRAP')
.switchMap(() =>
action$.filter(({ type }) => ['GET_JWT_SUCCESS', 'GET_JWT_FAILURE'].indexOf(type) !== -1)
.take(1)
.mergeMap(action => {
if (action.type === 'GET_JWT_FAILURE') {
return Observable.of({ type: 'BOOTSTRAP_FAILURE' });
}
return api.getProfileInfo()
.map(({ profile }) => ({
type: 'BOOTSTRAP_SUCCESS', profile,
}));
})
.startWith({ type: 'GET_JWT_REQUEST' })
);
When I try to test the bootstrapEpic in Jest with the following code;
const response = {};
const api = { getJWT: jest.fn() };
api.getJWT.mockReturnValue(Promise.resolve(response));
const action$ = ActionsObservable.of(actions.bootstrap());
const epic$ = epics.bootstrapEpic(action$, null, { api });
const result = await epic$.toArray().toPromise();
console.log(result);
The console.log call gives me the following output;
[ { type: 'GET_JWT_REQUEST' } ]
Somehow the getJwtEpic isn't called at all. I guess it has something to do with the action$ observable not dispatching the GET_JWT_REQUEST action but I can't figure out why. All help is so welcome!
Assuming actions.rehydrate() returns an action of type BOOTSTRAP and the gigya stuff is a typo,
getJwtEpic isn't called because you didn't call it yourself šŸ¤” When you test epics by manually calling them, then it's just a function which returns an Observable, without any knowledge of the middleware or anything else. The plumbing that connects getJwtEpic as part of the root epic, and provides it with (action$, store) is part of the middleware, which you're not using in your test.
This is the right approach, testing them in isolation, without redux/middleware. šŸ‘ So you test each epic individually, by providing it actions and dependencies, then asserting on the actions it emits and the dependencies it calls.
You'll test the success path something like this:
const api = {
getProfileInfo: () => Observable.of({ profile: 'mock profile' })
};
const action$ = ActionsObservable.of(
actions.rehydrate(),
{ type: 'GET_JWT_SUCCESS', idToken: 'mock token' }
);
const epic$ = epics.bootstrapEpic(action$, null, { api });
const result = await epic$.toArray().toPromise();
expect(result).toEqual([
{ type: 'GET_JWT_REQUEST' },
{ type: 'BOOTSTRAP_SUCCESS', profile: 'mock profile' }
]);
Then you'll test the failure path in another test by doing the same thing except giving it GET_JWT_FAILURE instead of GET_JWT_SUCCESS. You can then test getJwtEpic separately as well.
btw, ofType accepts any number of types, so you can just do action$.ofType('GET_JWT_SUCCESS', 'GET_JWT_FAILURE')

Resources