Hello fellow developers,
I'm creating a little app in react native, and i'm using react-redux to store and manage my datas. I've got an events.js reducer registered in my store, using 'events/load' dispatch action to set the initialState in my App.js. It looks like that :
import { auth, firestore } from '../lib/firebase'
const initialState = {
events: [],
userEvents: []
}
export default async function eventsReducer(state = initialState, action) {
switch (action.type) {
case 'events/load': {
const userId = auth.currentUser.uid
const eventsData = []
const userEventsData = []
// starts with other events
const queryEvents = await firestore.collection("events").where("userId", "!=", userId).get()
if (queryEvents) {
queryEvents.forEach(ev => {
eventsData.push(Object.assign({}, { id: ev.id }, ev.data()))
})
}
// user events
const userEvents = await firestore.collection("events").where("userId", "==", userId).get()
if (userEvents) {
userEvents.forEach(ev => {
userEventsData.push(Object.assign({}, { id: ev.id }, ev.data()))
})
}
return {
...state,
events: eventsData,
userEvents: userEventsData
}
}
case 'events/set': {
// Set the classics events
return {
...state,
events: action.payload
}
}
case 'userEvents/set': {
// Set the userEvents
return {
...state,
userEvents: action.payload
}
}
default:
return state
}
}
Now, I have a screen, using SectionList from react native, and I want to display there all the userEvents from my store, previously loaded. The problem is that my useSelector hook only return Promise when i try to get events. If i try to get events.userEvents, it's undefined.
Here what it look like :
const Manage = ({navigation}) => {
const [loading, setLoading] = React.useState(true);
const [events, setEvents] = React.useState([]);
const eventsSelector = useSelector((state) => state.events)
// use effect one time
useEffect(() => {
async function loadDatas() {
console.log('events selected', eventsSelector) // what i've got in my events ?
// get the datas for coming events
const storedEventsIds = []
const comingEvents = []
const passedEvents = []
if (eventsSelector !== null) {
eventsSelector.userEvents.forEach(event => { // <= THIS IS NOT WORKING - error : Cannot read properties of undefined (reading 'forEach')
// compare dates to know if it's a coming or passed event
const dateTimeStart = new Date(`${event.dateStart} ${event.timeStart}`)
const dateTimeEnd = new Date(`${event.dateEnd} ${event.timeEnd}`)
const dateNow = new Date()
if (!(doc.id in storedEventsIds)) {
if (compareAsc(dateTimeStart, dateNow) >= 0 ||
(compareAsc(dateTimeStart, dateNow) < 0 && compareAsc(dateTimeEnd, dateNow) >= 0)) {
comingEvents.push(Object.assign({}, event, {id: doc.id, passed: false}))
storedEventsIds.push(doc.id)
} else {
passedEvents.push(Object.assign({}, event, {id: doc.id, passed: true}))
storedEventsIds.push(doc.id)
}
}
})
}
// set events
setEvents([{
title: "Événements à venir",
data: comingEvents
}, {
title: "Événéments passés",
data: passedEvents
}]);
setLoading(false)
};
loadDatas()
}, [])
// ... omitted manage component code - basically only html
}
When i console log my eventsSelector this is what i've got, so you can see that I have some userEvents. But it's a Promise and i don't really know why...
events selected Promise {
"_U": 0,
"_V": 1,
"_W": Object {
"_U": 0,
"_V": 1,
"_W": Object {
"events": Array [],
"userEvents": Array [],
},
"_X": null,
"events": Array [],
"userEvents": Array [
Object {
"coverImage": null,
"dateEnd": "04/02/2022",
"dateStart": "04/11/2021",
"description": "Hbzbwkks
Sjjdjdjdjd
Wjdkkdieizhdbf
Sjjdjdjdjd",
"id": "ewPrFzAqQmusKrsqQnSP",
"maxLimit": null,
"online": true,
"openPrice": false,
"place": null,
"price": 50,
"secretPlace": false,
"timeEnd": "17:25",
"timeStart": "17:25",
"title": "Mon super event",
"userId": "pPL2g7bWDYZDzUGtlGCSyLOQ3WK2",
"website": "https://bloodbee.space",
},
Object {
"dateEnd": "05/10/2021",
"dateStart": "04/10/2021",
"description": "La teuf de l'année - 24h de folie ouais :)",
"id": "gSlmysO0KffkjGKyP9fR",
"maxLimit": 3000,
"online": false,
"openPrice": false,
"place": "Puy du pariou",
"price": 30,
"secretPlace": true,
"timeEnd": "23:00",
"timeStart": "23:00",
"title": "Rave party de l'année",
"userId": "pPL2g7bWDYZDzUGtlGCSyLOQ3WK2",
"website": null,
},
],
},
"_X": null,
}
How can I fix this ? Do you have any improvment suggestions ?
Thank you for your help :)
Edit :
Some code about my App.js
const AppWrapped = () => {
const [appIsReady, setAppIsReady] = useState(false);
const [signedIn, setSignedIn] = useState(false);
const dispatch = useDispatch()
const loadApp = async () => {
// Manage auth
await auth.onAuthStateChanged( async (user) => {
if (user) {
// Log the user
await dispatch({ type: 'users/set', payload: user })
// dispatch to get events
await dispatch({ type: 'events/load' })
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
setSignedIn(true)
} else {
// User is signed out
await dispatch({ type: 'users/set', payload: null })
setSignedIn(false)
}
})
}
if (!appIsReady) {
return (
<AppLoading
startAsync={loadApp}
onFinish={() => setAppIsReady(true)}
onError={console.warn}
autoHideSplash={true}
/>
)
} else {
// omitted html - when the app is ready - no more splashscreen
}
};
const App = () => {
return (
<StoreProvider store={store}>
<PaperProvider theme={theme}>
<AppWrapped />
</PaperProvider>
</StoreProvider>
)
};
did you dispatch the events/load action ?
/// src/redux/events/eventActions.js
import store from '../store'
function loadEvents() {
return {
type: 'events/load'
}
}
store.dispatch(loadEvents())
A good practice is to create a const for every action like this :
/// src/redux/events/eventTypes.js
const LOAD_EVENTS = 'events/load';
/// src/redux/events/eventActions.js
import store from '../store'
import { LOAD_EVENTS } from './eventTypes.js'
function loadEvents() {
return {
type: LOAD_EVENTS
}
}
store.dispatch(loadEvents())
/// src/redux/events/eventReducer.js
import { LOAD_EVENTS } from './eventTypes.js'
const initialState = {
events: [],
...
}
export default async function eventsReducer(state = initialState, action) {
switch (action.type) {
case LOAD_EVENTS: {
... Some code
return {
...state,
events: eventsData,
userEvents: userEventsData
}
}
default:
return state
}
}
Related
I want to call an API here
https://api.jikan.moe/v4/top/anime to get the data in raw format and then create an array of only useful data out of it. What is the reason the data is not being consoled
const initialAnime = {
anime: [],
genresLoaded: false,
genres: [],
};
function createAnimeFromRawData(rawData, animeArray) {
const data = rawData.data;
data.forEach((animeData) => {
const anime = {
mal_id: animeData.mal_id,
title: animeData.title,
title_english: animeData.title_english,
type: animeData.type,
episodes: animeData.episodes,
status: animeData.status,
duration: animeData.duration,
rating: animeData.rating,
rank: animeData.rank,
synopsis: animeData.synopsis,
};
console.log(animeArray);
animeArray.push(anime);
});
}
const RawdataAnime = async (api, genre, paging) => {
const Animearray = [];
for (let i = 1; Animearray.length < 60 && i < 10; i++) {
const {
data: { results },
} = await axios.get(`${api}`);
createAnimeFromRawData(results, Animearray);
}
return Animearray;
};
export const fetchAnime = createAsyncThunk(
"myanimelist/topAnime",
async (thunkAPI) => {
const {
myanimelist: { genres },
} = thunkAPI.getState();
return RawdataAnime(`https://api.jikan.moe/v4/top/anime`, genres, false);
}
);
const animeSlice = createSlice({
name: "Myanimelist",
initialState: initialAnime,
extraReducers: (builder) => {
builder.addCase(getGenresAnime.fulfilled, (state, action) => {
state.genres = action.payload;
state.genresLoaded = true;
});
builder.addCase(fetchAnime.fulfilled, (state, action) => {
state.anime = action.payload;
});
},
});
export const store = configureStore({
reducer: {
netflix: netflixSlice.reducer,
anime: animeSlice.reducer,
},
});
I tried the code above to get an array of only useful parts of data in the code but there was nothing in the console. There was no error and no output.
Whereas the response.data will be something similar to the json below::
{
"pagination":{
...
},
"data":[
...
],
"links":{
...
},
"meta":{
...
}
}
I believe the error is in the snippet
const { data: { results }} = await axios.get(`${api}`); // There are no results in the returned content
createAnimeFromRawData(results, Animearray);
Try something like
const { data } = await axios.get(`${api}`); // Equivalent to response.data
const results = data?.data || []
createAnimeFromRawData(results, Animearray);
I am having a problem with saving a value from the client to backend server, what it's suppose to do is on button click essentially save the previous value and increase it by one
VoteReducer
import {
FETCH_SERVERID,
FETCH_VOTE,
UPDATE_VOTE,
INCREASE_VOTE
} from './constants';
const initialState = {
serverID: '',
voteCount: '',
};
const voteReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_SERVERID:
return {
...state,
serverID: action.payload
};
case FETCH_VOTE:
return {
...state,
fetchVote: action.payload
};
case UPDATE_VOTE:
return {
...state,
voteCount: {
...state.voteCount, ...action.payload,
}
}
case INCREASE_VOTE:
return state + 1
default:
return state;
};
};
export default voteReducer;
Vote Actions
import { push } from 'connected-react-router';
import axios from 'axios';
import { success } from 'react-notification-system-redux';
import {
FETCH_SERVERID,
FETCH_VOTE,
UPDATE_VOTE,
INCREASE_VOTE
} from './constants';
import handleError from "../../utils/error";
export const fetchServerID = value => {
return {
type: FETCH_SERVERID,
payload: value
};
};
export const fetchVote = value => {
return {
type: FETCH_VOTE,
payload: value
};
};
export const increaseVote = () => {
return {
type: INCREASE_VOTE,
};
};
export const voteCount = vote => {
return async (dispatch, getState) => {
try {
const voteValues = {
serverID: "",
voteCount: + 1
}
const voteCount = localStorage.getItem('vote_button')
const newVote = {
votedServer: voteValues.serverID,
voteCount: voteValues.voteCount,
};
const successOptions = {
title: "Your vote has been succesfully recorded",
position: 'tr',
autodismiss: 1
};
const increasingVote = {
title: "We are increasing your vote count test ",
position: 'tr',
autodismis: 1
};
dispatch(success(increaseVote));
const response = await axios.post('/api/vote/add', newVote);
if(response.data.success === true) {
dispatch(success(successOptions));
} else {
const retryOptions = {
title: "Please properly vote on a server",
position: 'tr',
autodismiss: 1
};
dispatch(warning(retryOptions));
}
} catch (error) {
handleError(error, dispatch);
}
}
}
Server side code
const express = require('express');
const router = express.Router();
// Models/helpers
const Vote = require('../../models/vote');
router.post('/add', async (req, res) => {
const serverID = req.body.serverID;
const voteCount = req.body.voteCount;
const vote = new Vote({
serverID,
voteCount,
});
const voteSave = await vote.save();
await vote.save(async (err, data) => {
if (err) {
return res.status(400).json({
error: 'Your request could not be completed at this time'
});
}
res.status(200).json({
success: true,
message: 'We are now counting your vote!',
vote: voteSave
});
});
});
module.exports = router;
Vote Index
class Vote extends React.PureComponent {
render() {
const {
user,
vote,
voteCount,
} = this.props;
return (
<div className='vote-page'>
<button
variant='primary'
text='Vote'
className='vote_button'
onClick={() => { voteCount(vote) }}>
>Vote</button>
</div>
);
}
}
const mapStateToProps = state => {
return {
user: state.account.user,
voteCount: state.voteCount,
};
};
export default connect(mapStateToProps, actions)(Vote);
I literally started learning full stack development this week, uhm just trying to grasp advanced concepts but essentially what I want it to do is keep track of votes then increase it by one when a button is pressed/vote on from the client then passed to the server saved but increased by one every-time a user presses a vote on a specific page but I haven't got to the point of getting that to work nor have I gotten to the point of implementing a way for each type of listing to be different from one another when voted on
Hopefully someone answers soon! Thanks
I use next-redux-wrapper, MSW, #mswjs/data and redux-toolkit for storing my data in a store as well as mocking API calls and fetching from a mock Database.
I have the following scenario happening to me.
I am on page /content/editor and in the console and terminal, I can see the data was fetched from the mock database and hydrated from getStaticProps of Editor.js. So now IDs 1 to 6 are inside the store accessible.
Now I click on the PLUS icon to create a new project. I fill out the dialog and press "SAVE". a POST request starts, it's pending and then it gets fulfilled. The new project is now in the mock DB as well as in the store, I can see IDs 1 to 7 now.
Since I clicked "SAVE" and the POST request was successful, I am being routed to /content/editor/7 to view the newly created project.
Now I am on Page [id].js, which also fetched data from the mock DB and then it gets stored and hydrated into the redux store. The idea is, it takes the previous store's state and spreads it into the store, with the new data (if there are any).
Now the ID 7 no longer exists. And IDs 1 to 6 also don't exist anymore, instead, I can see in the console and terminal that IDs 8 to 13 were created, and the previous ones are no more.
Obviously, this is not great. When I create a new project and then switch the route, I should be able to access the newly created project as well as the previously created ones. But instead, they all get overwritten.
It either has something to do with the next-redux-wrapper or MSW, but I am not sure how to make it work. I need help with it. I will post some code now:
Code
getStaticProps
// path example: /content/editor
// Editor.js
export const getStaticProps = wrapper.getStaticProps(
(store) =>
async ({ locale }) => {
const [translation] = await Promise.all([
serverSideTranslations(locale, ['editor', 'common', 'thesis']),
store.dispatch(fetchProjects()),
store.dispatch(fetchBuildingBlocks()),
]);
return {
props: {
...translation,
},
};
}
);
// path example: /content/editor/2
// [id].js
export const getStaticProps = wrapper.getStaticProps(
(store) =>
async ({ locale, params }) => {
const { id } = params;
const [translation] = await Promise.all([
serverSideTranslations(locale, ['editor', 'common', 'thesis']),
store.dispatch(fetchProjects()),
// store.dispatch(fetchProjectById(id)), // issue: fetching by ID returns null
store.dispatch(fetchBuildingBlocks()),
]);
return {
props: {
...translation,
id,
},
};
}
);
Mock Database
Factory
I am going to shorten the code to the relevant bits. I will remove properties for a project, as well es helper functions to generate data.
const asscendingId = (() => {
let id = 1;
return () => id++;
})();
const isDevelopment =
process.env.NODE_ENV === 'development' || process.env.STORYBOOK || false;
export const projectFactory = () => {
return {
id: primaryKey(isDevelopment ? asscendingId : nanoid),
name: String,
// ... other properties
}
};
export const createProject = (data) => {
return {
name: data.name,
createdAt: getUnixTime(new Date()),
...data,
};
};
/**
* Create initial set of tasks
*/
export function generateMockProjects(amount) {
const projects = [];
for (let i = amount; i >= 0; i--) {
const project = createProject({
name: faker.lorem.sentence(faker.datatype.number({ min: 1, max: 5 })),
dueDate: date(),
fontFamily: getRandomFontFamily(),
pageMargins: getRandomPageMargins(),
textAlign: getRandomTextAlign(),
pageNumberPosition: getRandomPageNumberPosition(),
...createWordsCounter(),
});
projects.push(project);
}
return projects;
}
API Handler
I will shorten this one to GET and POST requests only.
import { db } from '../../db';
export const projectsHandlers = (delay = 0) => {
return [
rest.get('https://my.backend/mock/projects', getAllProjects(delay)),
rest.get('https://my.backend/mock/projects/:id', getProjectById(delay)),
rest.get('https://my.backend/mock/projectsNames', getProjectsNames(delay)),
rest.get(
'https://my.backend/mock/projects/name/:id',
getProjectsNamesById(delay)
),
rest.post('https://my.backend/mock/projects', postProject(delay)),
rest.patch(
'https://my.backend/mock/projects/:id',
updateProjectById(delay)
),
];
};
function getAllProjects(delay) {
return (request, response, context) => {
const projects = db.project.getAll();
return response(context.delay(delay), context.json(projects));
};
}
function postProject(delay) {
return (request, response, context) => {
const { body } = request;
if (body.content === 'error') {
return response(
context.delay(delay),
context.status(500),
context.json('Server error saving this project')
);
}
const now = getUnixTime(new Date());
const project = db.project.create({
...body,
createdAt: now,
maxWords: 10_000,
minWords: 7000,
targetWords: 8500,
potentialWords: 1500,
currentWords: 0,
});
return response(context.delay(delay), context.json(project));
};
}
// all handlers
import { buildingBlocksHandlers } from './api/buildingblocks';
import { checklistHandlers } from './api/checklist';
import { paragraphsHandlers } from './api/paragraphs';
import { projectsHandlers } from './api/projects';
import { tasksHandlers } from './api/tasks';
const ARTIFICIAL_DELAY_MS = 2000;
export const handlers = [
...tasksHandlers(ARTIFICIAL_DELAY_MS),
...checklistHandlers(ARTIFICIAL_DELAY_MS),
...projectsHandlers(ARTIFICIAL_DELAY_MS),
...buildingBlocksHandlers(ARTIFICIAL_DELAY_MS),
...paragraphsHandlers(ARTIFICIAL_DELAY_MS),
];
// database
import { factory } from '#mswjs/data';
import {
buildingBlockFactory,
generateMockBuildingBlocks,
} from './factory/buildingblocks.factory';
import {
checklistFactory,
generateMockChecklist,
} from './factory/checklist.factory';
import { paragraphFactory } from './factory/paragraph.factory';
import {
projectFactory,
generateMockProjects,
} from './factory/project.factory';
import { taskFactory, generateMockTasks } from './factory/task.factory';
export const db = factory({
task: taskFactory(),
checklist: checklistFactory(),
project: projectFactory(),
buildingBlock: buildingBlockFactory(),
paragraph: paragraphFactory(),
});
generateMockProjects(5).map((project) => db.project.create(project));
const projectIds = db.project.getAll().map((project) => project.id);
generateMockTasks(20, projectIds).map((task) => db.task.create(task));
generateMockBuildingBlocks(10, projectIds).map((block) =>
db.buildingBlock.create(block)
);
const taskIds = db.task.getAll().map((task) => task.id);
generateMockChecklist(20, taskIds).map((item) => db.checklist.create(item));
Project Slice
I will shorten this one as well to the relevant snippets.
// projects.slice.js
import {
createAsyncThunk,
createEntityAdapter,
createSelector,
createSlice,
current,
} from '#reduxjs/toolkit';
import { client } from 'mocks/client';
import { HYDRATE } from 'next-redux-wrapper';
const projectsAdapter = createEntityAdapter();
const initialState = projectsAdapter.getInitialState({
status: 'idle',
filter: { type: null, value: null },
statuses: {},
});
export const fetchProjects = createAsyncThunk(
'projects/fetchProjects',
async () => {
const response = await client.get('https://my.backend/mock/projects');
return response.data;
}
);
export const saveNewProject = createAsyncThunk(
'projects/saveNewProject',
async (data) => {
const response = await client.post('https://my.backend/mock/projects', {
...data,
});
return response.data;
}
);
export const projectSlice = createSlice({
name: 'projects',
initialState,
reducers: {
// irrelevant reducers....
},
extraReducers: (builder) => {
builder
.addCase(HYDRATE, (state, action) => {
// eslint-disable-next-line no-console
console.log('HYDRATE', action.payload);
const statuses = Object.fromEntries(
action.payload.projects.ids.map((id) => [id, 'idle'])
);
return {
...state,
...action.payload.projects,
statuses,
};
})
.addCase(fetchProjects.pending, (state, action) => {
state.status = 'loading';
})
.addCase(fetchProjects.fulfilled, (state, action) => {
projectsAdapter.addMany(state, action.payload);
state.status = 'idle';
action.payload.forEach((item) => {
state.statuses[item.id] = 'idle';
});
})
.addCase(saveNewProject.pending, (state, action) => {
console.log('SAVE NEW PROJECT PENDING', action);
})
.addCase(saveNewProject.fulfilled, (state, action) => {
projectsAdapter.addOne(state, action.payload);
console.group('SAVE NEW PROJECT FULFILLED');
console.log(current(state));
console.log(action);
console.groupEnd();
state.statuses[action.payload.id] = 'idle';
})
// other irrelevant reducers...
},
});
This should be all the relevant code. If you have questions, please ask them and I will try to answer them.
I have changed how the state gets hydrated, so I turned this code:
.addCase(HYDRATE, (state, action) => {
// eslint-disable-next-line no-console
console.log('HYDRATE', action.payload);
const statuses = Object.fromEntries(
action.payload.projects.ids.map((id) => [id, 'idle'])
);
return {
...state,
...action.payload.projects,
statuses,
};
})
Into this code:
.addCase(HYDRATE, (state, action) => {
// eslint-disable-next-line no-console
console.group('HYDRATE', action.payload);
const statuses = Object.fromEntries(
action.payload.projects.ids.map((id) => [id, 'idle'])
);
state.statuses = { ...state.statuses, ...statuses };
projectsAdapter.upsertMany(state, action.payload.projects.entities);
})
I used the adapter to upsert all entries.
I have this app which uses the first createAsyncThunk to get the first page from the API, then I want the second createAsyncThunk, which gets the next page, to fire when the user reaches the bottom of the page and get the data in the infinite scrolling method.
// Gets the First 10 Posts from the API
export const getPosts = createAsyncThunk(
"post/getPosts",
async (apiAddress) => {
const response = await fetch(apiAddress);
if (!response.ok) throw new Error("Request Failed!");
const data = await response.json();
return data;
}
);
// Loads the Next 10 Posts
export const getMorePosts = createAsyncThunk(
"post/getMorePosts",
async (apiAddress) => {
const response = await fetch(apiAddress);
if (!response.ok) throw new Error("Request Failed!");
const data = await response.json();
return data;
}
);
const redditPostSlice = createSlice({
name: "post",
initialState: {
redditPost: {},
isLoading: false,
hasError: false,
moreIsLoading: false,
moreHasError: false,
},
extraReducers: (builder) => {
builder
.addCase(getPosts.pending, (state) => {
state.isLoading = true;
state.hasError = false;
})
.addCase(getPosts.fulfilled, (state, action) => {
state.redditPost = action.payload.data;
state.isLoading = false;
state.hasError = false;
})
.addCase(getPosts.rejected, (state) => {
state.isLoading = false;
state.hasError = true;
})
.addCase(getMorePosts.pending, (state) => {
state.moreIsLoading = true;
state.moreHasError = false;
})
.addCase(getMorePosts.fulfilled, (state, action) => {
state.redditPost = action.payload.data;
state.moreIsLoading = false;
state.moreHasError = false;
})
.addCase(getMorePosts.rejected, (state) => {
state.moreIsLoading = false;
state.moreHasError = true;
});
},
});
My problem is that the state of the app changes to the second page and the first page contents are gone.
I know my problem is here state.redditPost = action.payload.data but I don't know how I can append this new state to the previous one.
I've been at this for hours and don't really know what to do anymore.
Is there any way to append the new state to the previous state?
I would assume that the payload data has an array of children. Like this example of response found online:
{
kind: "Listing",
data: {
...
children: [
{kind: "t3", data: {...}}
{kind: "t3", data: {...}}
{kind: "t3", data: {...}}
...
]
...
}
}
So you would need to make redditPost be an array. Also semantically is should be redditPosts to denote array.
initialState: {
redditPost: {},
...
and then when you're updating it one of the easiest ways is using ES6 spread
state.redditPost = {
...state.redditPost,
after: action.payload.data.after,
children: [
...state.redditPost.children,
...action.payload.data.children
]
}
Recently started using Hooks and, as cook as they are, they are giving me a bit of a headache.
I have a custom useFetch() hook that deals with fetching data from the API.
I also have a component where I need to use useFetch a few times and the results must be passed from one to another.
E.g.:
const ComponentName = () => {
const { responseUserInfo } = useFetch('/userinfo')
const { responseOrders } = useFetch(`/orders?id=${responseUserInfo.id}`)
const { isOrderRefundable } = useFetch(`/refundable?id={responseOrders.latest.id}`)
return <div>{isOrderRefundable}</div>
}
So, how do I actually "cascade" the hooks without creating 3 intermediate wrappers? Do I have to use HoC?
Your hook could return a callback, that when called does the API call:
const [getUserInfo, { userInfo }] = useFetch('/userinfo');
const [getOrders, { orders }] = useFetch(`/orders`)
const [getOrderRefundable, { isOrderRefundable }] = useFetch(`/refundable`);
useEffect(getUserInfo, []);
useEffect(() => { if(userInfo) getOrders({ id: userInfo.id }); }, [userInfo]);
useEffect(() => { if(orders) getOrderRefundable({ id: /*..*/ }); }, [orders]);
But if you always depend on the whole data being fetched, I'd just use one effect to load them all:
function useAsync(fn, deps) {
const [state, setState] = useState({ loading: true });
useEffect(() => {
setState({ loading: true });
fn().then(result => { setState({ result }); });
}, deps);
return state;
}
// in the component
const { loading, result: { userInfo, orders, isRefundable } } = useAsync(async function() {
const userInfo = await fetch(/*...*/);
const orders = await fetch(/*...*/);
const isRefundable = await fetch(/*...*/);
return { userInfo, orders, isRefundable };
}, []);