React custom hook is not updating from first click - javascript

I've created a custom hook to fetch data with events handlers, when I using it on click event the hook makes the request on the second click
useFetch.js
import { useState, useEffect } from 'react';
import { makeRequest } from '../utils';
const useFetch = (query = {}) => {
const [request, setRequest] = useState({ ...query });
const [data, setData] = useState({
response: null,
isError: null,
isLoading: request.isLoading,
});
const fetchData = async () => {
if (!request.url) {
return;
}
try {
const res = await makeRequest(
request.url,
request.method || 'get',
request.body || null,
);
setData({
response: res,
isLoading: false,
error: null,
});
} catch (error) {
setData({
response: null,
error,
isLoading: false,
});
}
};
const onEvent = (req) => {
if (req) {
setRequest({ ...req });
}
};
useEffect(() => fetchData(), [request]);
return { ...data, onEvent };
};
export default useFetch;
Component File
const { isLoading, isError, response, onEvent } = useFetch();
const ClickMe = () => {
onEvent({
url: 'v1/profile/login',
method: 'post',
body: {
username: 'eee#ddd.com',
password: '2342332',
},
});
console.log('response', response);
};
return (
<>
<button onClick={() => ClickMe()} type="button">
Click Me
</button>
)
the log inside the ClickMe function is null in the first click but in the second click it returns the value

Because fetchData is asynchronous function you cannot know when resposne will be set, that's why you cannot access it like normal sync code
in your app code you could observe response change to console it like
useEffect(() => { console.log(response) }, [ response ]);

At the time of console.log, the response is not fetched. Since when ever response changes, the component re-renders, you can try like below to see the updated values of isLoading and response.
return (
<>
{isLoading && <div> Loading... </div>}
{`Resonse is ${JSON.stringify(response)}`}
<button onClick={() => ClickMe()} type="button">
Click Me
</button>
</>
);

As the others said, it's an asynchronous operation. If you want to use the response as soon as you called onEvent, you can do something along these lines using a promise :
import { useState, useEffect } from 'react';
import { makeRequest } from '../utils';
const useFetch = (query = {}) => {
useEffect(() => {
if (query) {
fetchData(query)
}
}, []) // if query is provided, run query
const [data, setData] = useState({
response: null,
isError: null,
isLoading: true
});
const fetchData = async (query) => {
return new Promise((resolve, reject) => {
if (!query.url) {
reject('url needed')
}
makeRequest(query).then(res => {
setData({
response: res,
isLoading: false,
error: null
})
resolve(res)
).catch(error => {
setData({
response: null,
error,
isLoading: false
});
reject(error)
});
})
})
};
// provide fetchData directly for lazy calls
return { ...data, fetchData };
};
export default useFetch;
And then call it like so :
const { response, fetchData } = useFetch()
fetchData({
url: 'v1/profile/login',
method: 'post',
body: {
username: 'eee#ddd.com',
password: '2342332',
},
}).then(res => ...);

Related

React custom hook does not give result of POST

The custome hook post method working fine at the same time the response adding state taking time.
console.log(jsonResult)
shows the response of POST method at the same time responseData shows null
usePostQuery
import { useCallback, useState } from "react";
interface bodyData {
message: string,
author: string
}
const usePostQuery = (url: string, data?: bodyData )=> {
const [responseData, setResponseData] = useState();
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState('');
const callPost = useCallback( async (data: bodyData) => {
try {
setLoading(true);
const response = await fetch(url, {
method: "POST",
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
title: data.message,
userId: 15
})
});
const jsonResult = await response.json();
console.log('--------jsonResult---------');
console.log(jsonResult)
setResponseData(jsonResult);
} catch (error: any) {
setError(error.message);
} finally {
setLoading(false);
}
},
[url]
);
return { responseData, loading, error, callPost };
};
export default usePostQuery;
const { responseData, loading, error, callPost } = usePostQuery('https://jsonplaceholder.typicode.com/posts')
The responseData is not giving post call response
useEffect(() => {
if (draftMessage && myMessage) {
// submitNewMessage()
console.log("post my message to server");
callPost({
message: myMessage,
author: "Mo"
});
if (loading === false) {
setMyMessage("");
setdraftMessage(false);
console.log("after ", responseData);
}
console.log("responseData ", responseData);
}
}, [draftMessage, myMessage]);
The fetch is successful because the console in side fetch response shows the API response.
There's nothing wrong with your custom hook. The issue is in your effect hook.
It only triggers when its dependencies change, ie draftMessage and myMessage. It does not re-evaluate loading or responseData so will only ever see their states at the time it is triggered.
It's really unclear what you're using the draftMessage state for. Instead, I would simply trigger the callPost in your submit handler...
export default function App() {
const [myMessage, setMyMessage] = useState("");
const { responseData, loading, callPost } = usePostQuery(
"https://jsonplaceholder.typicode.com/posts"
);
const handleMyMessage = (val) => {
setMyMessage(val);
};
const handleSubmit = async (event) => {
event.preventDefault();
await callPost({ message: myMessage, author: "Mo" });
setMyMessage("");
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
{loading ? (
<p>Loading...</p>
) : (
<ChatForm
onChange={handleMyMessage}
myMessage={myMessage}
handleSubmit={handleSubmit}
/>
)}
<pre>responseData = {JSON.stringify(responseData, null, 2)}</pre>
</div>
);
}
Your hook controls the loading and responseData states so there's really very little for your components to do.

Database updates only on the first click of the button with this functions, what is wrong and how could i fix it?

I am new to react and MongoDB, I am trying to add months to a date in my database in mongo, but it only updates the first time I click on the <Price> button, I need it to update every time I click it. The user has to log out and log back in for it to work again, but still only 1 update can be made to the database. Can someone explain to me why this is happening, and how could it be fixed?
This is the function
import React, { useContext } from "react";
import { useState } from "react";
import useFetch from "../../hooks/useFetch";
import Footer from "../../components/Footer";
import Navbar from "../../components/Navbar";
import Sidebar from "../../components/Sidebar";
import {
ContractContainer,
HeadingContainer,
TypeH1,
ActiveUntil,
MonthlyWrapper,
MonthlyContainer,
MonthNumber,
Price,
Navbarback,
} from "./userinfoElements";
import { AuthContext } from "../../context/AuthContext";
import moment from "moment";
import axios from "axios";
const Userinfo = () => {
// for nav bars
const [isOpen, setIsOpen] = useState(false);
// set state to true if false
const toggle = () => {
setIsOpen(!isOpen);
};
const { user } = useContext(AuthContext);
let { data, loading, reFetch } = useFetch(`/contracts/${user.contractType}`);
let dateFormat = moment(user.activeUntil).format("DD/MMMM/yyyy");
const updateDate = async () => {
try {
let newDate = moment(user.activeUntil).add(1, "months");
dateFormat = newDate.format("DD/MMMM/yyyy");
axios.put(`/activedate/${user.namekey}`, {
activeUntil: newDate,
});
} catch (err) {
console.log(err);
}
reFetch();
};
return (
<>
<Sidebar isOpen={isOpen} toggle={toggle} />
{/* navbar for smaller screens*/}
<Navbar toggle={toggle} />
<Navbarback /> {/* filling for transparent bacground navbar*/}
{loading ? (
"Loading components, please wait"
) : (
<>
<ContractContainer>
<HeadingContainer>
<TypeH1>{data.contractType}</TypeH1>
<ActiveUntil>Subscription active until {dateFormat}</ActiveUntil>
</HeadingContainer>
<MonthlyWrapper>
<MonthlyContainer>
<MonthNumber>1 Month</MonthNumber>
<Price onClick={updateDate}>{data.month1Price}$</Price>
</MonthlyContainer>
<MonthlyContainer>
<MonthNumber>3 Month</MonthNumber>
<Price onClick={updateDate}>{data.month3Price}$</Price>
</MonthlyContainer>
<MonthlyContainer>
<MonthNumber>6Month</MonthNumber>
<Price onClick={updateDate}>{data.month6Price}$</Price>
</MonthlyContainer>
<MonthlyContainer>
<MonthNumber>12Month</MonthNumber>
<Price onClick={updateDate}>{data.month12Price}$</Price>
</MonthlyContainer>
</MonthlyWrapper>
</ContractContainer>
</>
)}
<Footer />
</>
);
};
export default Userinfo;
this is the fetch hook
import { useEffect, useState } from "react";
import axios from "axios";
const useFetch = (url) => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const res = await axios.get(url);
setData(res.data);
} catch (err) {
setError(err);
}
setLoading(false);
};
fetchData();
}, [url]);
const reFetch = async () => {
setLoading(true);
try {
const res = await axios.get(url);
setData(res.data);
} catch (err) {
setError(err);
}
setLoading(false);
};
return { data, loading, error, reFetch };
};
export default useFetch;
Any help is appreciated!
EDIT: added AuthContext file and server sided controllers if needed
import React from "react";
import { createContext, useEffect, useReducer } from "react";
const INITIAL_STATE = {
user: JSON.parse(localStorage.getItem("user")) || null,
loading: false,
error: null,
};
export const AuthContext = createContext(INITIAL_STATE);
const AuthReducer = (state, action) => {
switch (action.type) {
case "LOGIN_START":
return {
user: null,
loading: true,
error: null,
};
case "LOGIN_SUCCESS":
return {
user: action.payload,
loading: false,
error: null,
};
case "LOGIN_FAILURE":
return {
user: null,
loading: false,
error: action.payload,
};
case "LOGOUT":
return {
user: null,
loading: false,
error: null,
};
default:
return state;
}
};
export const AuthContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(AuthReducer, INITIAL_STATE);
useEffect(() => {
localStorage.setItem("user", JSON.stringify(state.user));
}, [state.user]);
return (
<AuthContext.Provider
value={{
user: state.user,
loading: state.loading,
error: state.error,
dispatch,
}}
>
{children}
</AuthContext.Provider>
);
};
api Controller to update active date
import User from "../models/User.js";
export const updateActiveDate = async (req, res, next) => {
try {
await User.updateOne({ $set: { activeUntil: req.body.activeUntil } });
res.status(200).json("Active date has been updated.");
} catch (err) {
next(err);
}
};
api Controller to find contracts
import Contracts from "../models/Contracts.js";
export const getContract = async (req, res, next) => {
try {
const Contract = await Contracts.findOne({
contractType: req.params.contractType,
});
res.status(200).json(Contract);
} catch (err) {
next(err);
}
};
api Controller for login authentication
export const login = async (req, res, next) => {
try {
const user = await User.findOne({ namekey: req.body.namekey });
if (!user) return next(createError(404, "User not found!"));
if (req.body.password === undefined) {
return next(createError(500, "Wrong password or namekey!"));
}
const isPasswordCorrect = await bcrypt.compare(
req.body.password,
user.password
);
if (!isPasswordCorrect)
return next(createError(400, "Wrong password or namekey!"));
const token = jwt.sign({ id: user._id }, process.env.JWT);
const { password, ...otherDetails } = user._doc;
res
.cookie("access_token", token, {
httpOnly: true,
})
.status(200)
.json({ details: { ...otherDetails } });
} catch (err) {
next(err);
}
};
You should update the stored user state to reflect the activeUntil date change.
Define a 'UPDATE_USER_DATE' action in your reducer to update the user instance:
case "UPDATE_USER_DATE":
const updatedUser = { ...state.user };
updatedUser.activeUntil = action.payload;
return {
...state,
user: updatedUser
};
Then, after updating the date in updateDate, update the user state as well:
const { user, dispatch } = useContext(AuthContext);
const updateDate = async () => {
try {
let newDate = moment(user.activeUntil).add(1, "months");
dateFormat = newDate.format("DD/MMMM/yyyy");
await axios.put(`/activedate/${user.namekey}`, {
activeUntil: newDate,
});
dispatch({ type: "UPDATE_USER_DATE", payload: newDate });
} catch (err) {
console.log(err);
}
reFetch();
};
Give this a try. It awaits the put request, and only once that has responded it calls reFetch. Without the await you're calling the reFetch before the put request has had a chance to complete its work.
const updateDate = async () => {
try {
let newDate = moment(user.activeUntil).add(1, "months");
dateFormat = newDate.format("DD/MMMM/yyyy");
await axios.put(`/activedate/${user.namekey}`, {
activeUntil: newDate,
});
} catch (err) {
console.log(err);
} finally {
reFetch();
}
};

How to manage component states (loading, error, data) in React

I have implemented the following code to fetch data and render a component if everything goes well along with checking loading, error states.
import { useEffect, useState } from "react";
function Posts() {
const [posts, setPosts] = useState([]);
const [loader, setLoader] = useState(false);
const [error, setError] = useState({ status: false, message: "" });
const fetchPosts = () => {
setLoader(true);
setTimeout(async () => {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/posts"
);
// throw new Error("Some error occured");
const data = await response.json();
if (data.error) {
setError({ status: true, message: data.error });
} else {
setPosts(data);
}
setLoader(false);
} catch (error) {
console.log("error", error);
setError({ status: true, message: error.message });
setLoader(false);
}
}, 2000);
};
useEffect(() => {
fetchPosts();
}, []);
if (loader) return <h3>Loading...</h3>;
if (error.status) return <h3>Error: {error.message}</h3>;
return (
<div>
<h1>Posts</h1>
{posts.length === 0 && <h3>There are no posts</h3>}
{posts.length > 0 && (
<div>
{posts.map((post) => (
<Post post={post} key={post.id} />
))}
</div>
)}
</div>
);
}
export default Posts;
Is this the right way to handle loading, error and success states when fetching data? or is there a better and more elegant solution than repeating this for every component?
Instead of checking for data.error in the try block, you could check for response.ok; if it is true, call response.json(), otherwise throw an error.
Also move the setLoader call to the finally block to avoid the duplicate calls in try and catch blocks.
try {
const response = await fetch(...);
if (response.ok) {
let data = await response.json();
setPosts(data);
} else {
throw new Error(/* error message */);
}
} catch (error) {
console.log("error", error);
setError({ status: true, message: error.message });
} finally {
setLoader(false);
}
If you want to check for data.error property in a response, you can change the following if condition
if (response.ok) {
to
if (response.ok && !data.error) {
is there a better and more elegant solution than repeating this for
every component?
Make a custom hook to make the fetch request and use that in every component that needs to fetch data from the backend.
const useFetch = (apiUrl, initialValue) => {
const [data, setData] = useState(initialValue);
const [loader, setLoader] = useState(false);
const [error, setError] = useState({ status: false, message: "" });
useEffect(() => {
async function fetchData = (url) => {
setLoader(true);
try {
const response = await fetch(url);
if (response.ok) {
let responseData = await response.json();
setData(responseData);
} else {
throw new Error(/* error message */);
}
} catch (error) {
console.log("error", error);
setError({ status: true, message: error.message });
} finally {
setLoader(false);
}
}
fetchData(apiUrl);
}, [apiUrl]);
return [data, error, loader];
};
Your solution should be good enough to do, but, to me, I would prefer not to set timeout for getting data, and I will use .then and .catch for better readable and look cleaner to me
import { useEffect, useState } from "react";
function Posts() {
const [posts, setPosts] = useState([]);
const [loader, setLoader] = useState(false);
const [error, setError] = useState({ status: false, message: "" });
const fetchPosts = () => {
fetch("https://jsonplaceholder.typicode.com/posts")
.then(response => response.json())
.then(data => {
setPosts(data);
setLoader(false);
})
.catch(error =>{
console.log("error", error);
setError({ status: true, message: error.message });
setLoader(false);
});
};
useEffect(() => {
setLoader(true);
fetchPosts();
}, []);
if (loader) return <h3>Loading...</h3>;
if (error.status) return <h3>Error: {error.message}</h3>;
return (
<div>
<h1>Posts</h1>
{posts.length === 0 && <h3>There are no posts</h3>}
{posts.length > 0 && (
<div>
{posts.map((post) => (
<Post post={post} key={post.id} />
))}
</div>
)}
</div>
);
}
export default Posts;

react hook useEffect to fetch data on button click (typescript)

I have a component and I want to fetch isbn data on button click using react hook useEffect, performing a get on the route ${basicUrl}/editorials/${isbn}, so i wrote this component:
import React, { Fragment, useState } from "react";
import "./Home.css";
import { V3_BASIC_URL } from "../../constants/endpoints";
import { useDataApi } from "../../store/effects/dataEffects";
import SearchIsbnElement from "../../components/SearchIsbnElement/SearchIsbnElement";
import IsbnPanelElement from "../../components/IsbnPanelElement/IsbnPanelElement";
function Home() {
const [query, setQuery] = useState<string>("9788808677853");
const [isValid, setIsValid] = useState<boolean>(true);
const url = `${V3_BASIC_URL(
process.env.REACT_APP_API_ENV
)}/editorials/${query}`;
const [{ isbn, isLoading, isError }, doFetch] = useDataApi(url, {
isLoading: false,
isError: false,
isbn: undefined,
});
const buttonCallback = () => {
doFetch(url);
};
const isbnRegexp = /^97\d{11}$/
const validateQuery = (query: string): boolean => isbnRegexp.test(query)
const inputCallback = (query: string) => {
setQuery(query)
setIsValid(validateQuery(query));
};
return (
<div id="isbn-panel-home" className="Home">
<SearchIsbnElement
inputCallback={inputCallback}
buttonCallback={buttonCallback}
query={query}
isValid={isValid}
></SearchIsbnElement>
{isError && <div>Il servizio al momento non è disponibile, riprova più tardi</div>}
{isLoading ? (
<div>Loading ...</div>
) : (
!isError &&
<Fragment>
<IsbnPanelElement isbn={isbn}></IsbnPanelElement>
<p>{isbn?.scheda_volume == null && 'isbn non trovato'}</p>
</Fragment>
)}
</div>
);
}
export default Home;
the useDataApi function uses the hook useEffect and returns state and setUrl action to set the new url on isbn value change. This is the useDataApi file:
import { useState, useEffect, useReducer } from "react";
import {
dataFetchFailure,
dataFetchInit,
dataFetchSuccess,
} from "../actions/dataActions";
import { dataFetchReducer, ISBNState } from "../reducers/dataReducers";
import { get } from "../../tools/request";
type InitialState = {
isLoading: boolean,
isError: boolean,
isbn: undefined,
}
export const useDataApi = (initialUrl: string, initialData: InitialState) : [ISBNState, (value: string) => void] => {
const [url, setUrl] = useState(initialUrl);
const [state, dispatch] = useReducer(dataFetchReducer, initialData);
useEffect(() => {
let didCancel: boolean = false;
const fetchData = async (): Promise<any> => {
dispatch(dataFetchInit());
const options = {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
auth: {
username: `${process.env.REACT_APP_API_AUTH_USER}`,
password: `${process.env.REACT_APP_API_AUTH_PWD}`
}
}
try {
const {data} = await get(url, options);
if (!didCancel) {
dispatch(dataFetchSuccess(data));
}
} catch (error) {
if (!didCancel) {
dispatch(dataFetchFailure(error));
}
}
};
fetchData();
return () => {
didCancel = true;
};
}, [url]);
return [state, setUrl];
};
with this code fetching starts on page load, but i want to fetch data only on button click. How can I do this?
useEffect() is a hook to manipulate the component through the different lifecycle methods. In order to do something onClick you need to create a method for that:
const fetchData = async (): Promise<any> => {
dispatch(dataFetchInit());
const options = {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
auth: {
username: `${process.env.REACT_APP_API_AUTH_USER}`,
password: `${process.env.REACT_APP_API_AUTH_PWD}`
}
}
try {
const {data} = await get(url, options);
if (!didCancel) {
dispatch(dataFetchSuccess(data));
}
} catch (error) {
if (!didCancel) {
dispatch(dataFetchFailure(error));
}
}
};
Just do that and you will be fine
Edit: the new version of useDataApi
export const useDataApi = (
url: string,
initialData: InitialState
): [ISBNState, (value: string) => void] => {
const [state, dispatch] = useReducer(dataFetchReducer, initialData);
const fetchData = useCallback(async (): Promise<any> => {
dispatch(dataFetchInit());
const options = {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
auth: {
username: `${process.env.REACT_APP_API_AUTH_USER}`,
password: `${process.env.REACT_APP_API_AUTH_PWD}`,
},
};
try {
const { data } = await get(url, options);
dispatch(dataFetchSuccess(data));
} catch (error) {
dispatch(dataFetchFailure(error));
}
}, [url]);
return [state, fetchData];
};
The useDataApi hook returns [,doFetch], but the doFetch is actually setUrl so if you wanted that to work as expected you can let the initial value for the url be null or falsey and only allow a fetch inside the effect when the url is valid/truthy. When you click the button, thats when you setUrl and that's when the effect will allow a fetchData to occur because by then the value of url will be set.
export const useDataApi = (initialUrl: string, initialData: InitialState): [ISBNState, (value: string) => void] => {
// make this default to null here, or where you intende to use this hook
const [url, setUrl] = useState(null);
// custom hook body
useEffect(() => {
// effect body
if (url) {
fetchData();
}
// hook cleanup
}, [url]);
return [state, setUrl];
};
Although, the better solution is directly calling the function fetchData on the button click. One way you can do that is by modifying your useDataApi hook to return 'fetchData' directly allowing it to accept the url as an argument and removing the need for the const [url,setUrl] = useState(initialUrl) entirely
export const useDataApi = (initialUrl: string, initialData: InitialState): [ISBNState, (value: string) => void] => {
const [state, dispatch] = useReducer(dataFetchReducer, initialData);
const fetchData = useCallback(async (url): Promise<any> => {
dispatch(dataFetchInit());
const options = {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
auth: {
username: `${process.env.REACT_APP_API_AUTH_USER}`,
password: `${process.env.REACT_APP_API_AUTH_PWD}`,
},
};
try {
const { data } = await get(url, options);
if (!didCancel) {
dispatch(dataFetchSuccess(data));
}
} catch (error) {
if (!didCancel) {
dispatch(dataFetchFailure(error));
}
}
}, []);
return [state, fetchData];
};
You can also drop initialUrl from the hook useDataApi

How to run useEffect part first in React-Hooks?

How to execute the axios part and send the updated states props to Important component.
When I console.log I see that state passed as props with an empty object but after a fraction of seconds again states is updated with a new fetched value that means my return is running first then my usEffect axios part is running,
How can I make sure that axios part should run first then my return part. In first go updated part should be sent not the blank empty part
const initialState = {
Important: [{}],
Error: false
}
const reducer = (state, action) => {
switch (action.type) {
case "STEPFIRST":
return {
...state,
Important: action.payload,
};
case "STEPSecond":
return {
Error: true,
};
default:
return state;
}
}
const Landing = () => {
const [states, dispatch] = useReducer(reducer, initialState)
console.log(states)
useEffect(() => {
axios.get("https://example.com/")
.then(response => {
dispatch({
type: "STEPFIRST",
payload: response.data
});
})
.catch(error => {
dispatch({
type: "STEPSecond"
});
});
},[]);
const [xyz, xyzfn] = useState();
console.log(xyz)
return (
<div>
<Important states = {states} xyzfn={xyzfn} />
<Foo xyz={xyz}/>
</div>
);
};
export default Landing;
useEffect will always run after first rendering is done. You can have a loading state in your state and return the component accordingly.
const initialState = {
Important: [{}],
Error: false,
isLoading: true
}
const reducer = (state, action) => {
switch (action.type) {
case "STEPFIRST":
return {
...state,
Important: action.payload,
isLoading: false
};
case "STEPSecond":
return {
Error: true,
isLoading: false
};
default:
return state;
}
}
const Landing = () => {
const [states, dispatch] = useReducer(reducer, initialState)
console.log(states)
useEffect(() => {
axios.get("https://example.com/")
.then(response => {
dispatch({
type: "STEPFIRST",
payload: response.data
});
})
.catch(error => {
dispatch({
type: "STEPSecond"
});
});
},[]);
const [xyz, xyzfn] = useState();
console.log(xyz)
if(state.isLoading){
return <div>Loading....</div>
}
return (
<div>
<Important states = {states} xyzfn={xyzfn} />
<Foo xyz={xyz}/>
</div>
);
};
useEffect callback runs after the render phase.
Also, fetch calls are asynchronous, so you want to use conditional rendering:
const Landing = () => {
const [states, dispatch] = useReducer(reducer, initialState);
useEffect(() => {
axios
.get("https://example.com/")
.then((response) => {
dispatch({
type: "STEPFIRST",
payload: response.data,
});
})
.catch((error) => {
dispatch({
type: "STEPSecond",
});
});
}, []);
// Use any comparison function to indicate that `states` changed.
// like deep comparison function `isEqual` from lodash lib.
return (
<div>
{!lodash.isEqual(states, initialState) && (
<Important states={states} xyzfn={xyzfn} />
)}
</div>
);
};

Categories