The fetch call is asynchronous. This means it is not guaranteed to be complete before the program enters the next line.
Because of this the blogs array will be empty at the first render. You can add an check in the src CardItem component to only use the value returned from the fetch call when it is available:
<CardItem
src={blogs.length > 0 ? blogs[0].mainImage : ''}
...
/>
An alternative would be to use the fact that blogs is an array and use the map operator to build one or more CardItems.
<ul className='cards-items'>
{blogs.map(blog => <CardItem
src={blog.mainImage}
...
/>)}
</ul>
Answer from MaartenDev on Stack OverflowThe fetch call is asynchronous. This means it is not guaranteed to be complete before the program enters the next line.
Because of this the blogs array will be empty at the first render. You can add an check in the src CardItem component to only use the value returned from the fetch call when it is available:
<CardItem
src={blogs.length > 0 ? blogs[0].mainImage : ''}
...
/>
An alternative would be to use the fact that blogs is an array and use the map operator to build one or more CardItems.
<ul className='cards-items'>
{blogs.map(blog => <CardItem
src={blog.mainImage}
...
/>)}
</ul>
I faced the same problem, and here is how I solved it..
First, I created a loading state, and set the initial state to true.
// const [singlePackage, setPackage] = useState([]);
const [isLoading, setLoading] = useState(true);
then, in the useEffect hook, I set the state to false like so..
useEffect(() => {
axios.get(baseURL).then((response) => {
setPackage(response.data);
setLoading(false);
})
}, []);
Then, I used a condition, if the loading state is true, return the spinner else return the component like so...
if (isLoading) {
return (
<div className="loadingContainer">
<Loader
type="ThreeDots"
color="#00b22d"
height={100}
width={100}
//3 secs
/>
</div>
)
} else {
return (
// your code here
)}
I am using react-loader-spinner, and just styled the container
you can install it using...
npm install react-loader-spinner --save
the style for container ...
.loadingContainer{
position: fixed;
top: 50%;
left:50%;
transform: translate(-50%,50%);
}
Hello. Title was hard to put together haha.
So, this is the deal.
I'm making a website that has a header with login and register options. If the user already logged in and the data is stored in the cookies, instead of the login and register options it should display a logout option.
I have the following:
A context for the user auth.
First i had the initialState setted as in the 'SET_INITIAL_VALUES' case. But this gave me hydration issues because the server had no access to the cookies so the final document was different. (tried solving this using getServerSideProps but that gave me another issues).
In order for this to work i had to make the 'SET_INITIAL_VALUES' changes in an useEffects.
This is the Header component.
So, i use the state of userInfo to know if the user logged in previously.
This all works but when i login and then refresh the page, i can see the effect from the rerender. That is, i first see the login and register options but after half a second it changes to the proper logout option. Is there a better way to deal with this and solve this issue?
I'm fairly new to Nextjs.
You might want to try adding conditional logic within the useEffect so you only trigger the dispatch if you don't already have a profile.
import "./styles.css";
import { useDispatch, useSelector } from "react-redux";
import { useEffect, useCallback } from "react";
import { getCurrentProfile } from "./action";
export const Profile = () => {
const dispatch = useDispatch();
const profileReducer = useSelector((state) => state.profile);
const authReducer = useSelector((state) => state.auth);
const { profile, error, loading } = profileReducer;
// read more about this here: https://stackoverflow.com/questions/58624200/react-hook-useeffect-has-a-missing-dependency-dispatch
const stableDispatch = useCallback(dispatch, []);
useEffect(() => {
if (!profile) {
stableDispatch(getCurrentProfile());
}
}, [profile, stableDispatch]);
const { user } = authReducer;
console.log("loading", loading);
console.log("profile", profile);
return loading && profile === null ? <div>Spinner</div> : "Actual Profile";
};
export default Profile;
Also, it doesn't seem like you're currently doing anything with the loading piece of state–at least from what you've shared here. You might want to dispatch an action indicating that you're loading before you start the fetch and then it will be set to false when you get the response.
Check out this codesandbox for reference: https://codesandbox.io/s/focused-kilby-gd2nr?file=/src/App.js
Reducers:
const initialState = {
profile: null,
loading: false
};
export const profile = (state = initialState, action) => {
const { type, payload } = action;
switch (type) {
case "LOADING_PROFILE":
return {
...state,
loading: true
};
case "GET_PROFILE":
return {
...state,
profile: payload,
loading: false
};
case "PROFILE_ERROR":
return {
...state,
error: payload,
profile: null
};
case "CLEAR_PROFILE":
return {
...state,
profile: null,
loading: false
};
default:
return state;
}
};
export const auth = (state = {}, action) => {
return state;
};
Action Creator:
import axios from "axios";
export const getCurrentProfile = () => async (dispatch) => {
try {
dispatch({ type: "LOADING_PROFILE" });
const res = await axios.get("https://jsonplaceholder.typicode.com/users/1");
console.log(res);
dispatch({
type: "GET_PROFILE",
payload: res.data.data
});
} catch (err) {
dispatch({
type: "PROFILE_ERROR",
payload: { msg: err.response.statusText, status: err.response.status }
});
}
};
index.js
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore, combineReducers, applyMiddleware } from "redux";
import { profile, auth } from "./reducers";
import App from "./App";
import thunk from "redux-thunk";
const store = createStore(
combineReducers({
profile,
auth
}),
applyMiddleware(thunk)
);
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<Provider store={store}>
<App />
</Provider>
</StrictMode>,
rootElement
);
Well i solved it by dispatching 'getCurrentProfile' not 'getCurrentProfile()' turns out using it like a function causes continuously firing off.
const profileReducer = useSelector((state) => state.profile);
const authReducer = useSelector((state) => state.auth);
const { profile, error, loading } = profileReducer;
const dispatch = useDispatch();
useEffect(() => {
if (!profile) {
console.log("It worked")
dispatch(getCurrentProfile());
}
}, [dispatch(getCurrentProfile)])
So I have a log in page with a login form and a user page with a logout button.
I'm using useEffect on both the loginpage and userpage.
My logout button and useeffect works fine and looks like this. It goes back to the home page if I logout from the userpage
useEffect(()=> {
if(!userInfoFromStorage){
history("/")
} }, [userInfoFromStorage, history])
In my log in page however, after putting in the correct details for login. The submit button doesnt do anything until I refresh the page manually, which then takes me to my userpage. the useeffects in login looks like this:
useEffect(()=> {
if(userInfoFromStorage && userInfoFromStorage.isAdmin){
history("/user")
}}, [userInfoFromStorage, history])
Try
useEffect(() => getUsers(),[Details]);
Adding your state as the dependency should allow it run every time the state changes
The useEffect hook with an empty dependency array will only run in the onMount lifecycle phase of the component, that means only once. If you want it to run on every render, remove the dependency array like:
useEffect(() => getUsers());
Hi there!
I have been looking for a way to solve this issue for a while, and I have a hard time googling it.
Pseudo code example :
useEffect(() => {
setCopy(Data)
}, [Switch]); It's not exactly what I'm doing, but the principale is the same.
I would like to put Data in Copy, but only when Switch change.
The issue is, I have to put Data in the dependency array or I get eslint warning. But I can't do that, because it will run the effect when data is changing, and I don't want that.
I tried to use a callback, but same issue, the callback will change each time data change, and will launch the effect.
Is there a way to do this? I have been trying many things, but can't find a solution.
And I don't want to disable ESLint.
Thank you!
Since you are mutating the original node element in place instead of updating it in an immutable manner, setChartData isn't triggering a re-render as react checks if the value passed to state updater is actually changed from previous value or not and since the reference is same it believes that nothing has changed
According to React principles you must never mutate state values instead update them in an immutable way.
If its possible for you to update the state in an immutable manner, please do that in toggleNodeByRank
However you can still work around by shallow cloning the node like
const toggleNodeByRank = async (nodeArg, rank) => {
const node = {...nodeArg};
if (node && node.data) {
return node;
}
};
After doing the above change, it would trigger a re-rerender but you must know that it isn't the best way
You have problem with understanding promises, in you code this 2 console.log will output same value because (data) => setChartData(data) is callback witch will be called after toggleNodeByRank is done.
Basically your code executes in this way:
1) console.log('===PreviousState===', chartData);
2) toggleNodeByRank(chartData, rank); //here you just send request
3) console.log('===StateUpdated===', chartData);
4) (data) => setChartData(data)
So everything is fine and second time when u run your component update you see changed chartData