I am getting Null value on my props on initial render - javascript

My problem is I am having this output when I run/refresh my system
{user: Array(0)}
I use props to send my user info on my other components
Here is my App.js code
function App() {
const [user, setUser] = useState([])
const token = localStorage.getItem('mytoken')
let navigate = useNavigate()
useEffect(() => {
...
.then(result => setUser(result))
},[token])
return (
<div>
<Header user = {user}/>
<Routes>
<Route>
<Route path='/homepage' element = {<UserHomePage user = {user}/>} ></Route>
</Route>
</Routes>
</div>
);
And here is my other UserHomePage.js
function UserHomePage(props) {
console.log(props)
return (
<div>
</div>
);
}

You are setting the state to an array, but you are sending an object to the component.
setUser(result)
should be
setUser(result.user)

Put const token = localStorage.getItem('mytoken') inside of the useEffect and remove the [token]
Because the token variable is not a state, it is a static variable, useEffect trigger point runs on state change, your token variable is not a state. A quick fix to this is empty the square brackets

Related

localStorage removing elements in array after browser refresh

I have a react app and I want to persist the array of favorites when the page refreshes.
The data is set correctly, I can see it in the dev tools. But when i refresh the page, the data is removed. Any ideas why this may be?
Link to sandbox - https://codesandbox.io/s/sad-surf-sqgo0q?file=/src/App.js:368-378
const App = () => {
const [favourites, setFavourites] = useState([]);
useEffect(() => {
localStorage.setItem("favourites", JSON.stringify(favourites));
}, [favourites]);
useEffect(() => {
const favourites = JSON.parse(localStorage.getItem("favourites"));
if (favourites) {
setFavourites(favourites);
}
}, []);
return (
<FavContext.Provider value={{ favourites, setFavourites }}>
<HashRouter>
<Routes>
<Route path={"/"} element={<Dashboard />} />
<Route path={"/favorites"} element={<Favorites />} />
</Routes>
</HashRouter>
</FavContext.Provider>
);
};
export default App;
Make sure to set item if array is not empty
useEffect(() => {
if(favourites.length) localStorage.setItem("favourites", JSON.stringify(favourites));
}, [favourites]);
Yes, it is because when you reload the app the useEffect will trigger and you have an empty array in your favorite for the first time so it set the empty array in local storage.
You can fix it by adding a simple check
useEffect(() => {
if(favourites.length > 0){
localStorage.setItem("favourites", JSON.stringify(favourites));
}
}, [favourites]);
By this setItem only work when there is something in the favorites state

Can not update a component while rendering a different component

I am using the useAuth hook to my authContext, so that I can use hook to set global states for my components.
Now, I am trying to navigate to the '/' page if the user was not logged in, and also display a requireLoginAlert to the user in the '/' page after the user was redirected. However, I got the following error when I try to include the setRequireLoginAlert(true) function in requireAuth.js.
Warning: Cannot update a component (`AuthProvider`) while rendering a different component (`RequireAuth`). To locate the bad setState() call inside `RequireAuth`
My requireAuth.js:
const RequireAuth = () => {
const { auth, setRequireLoginAlert } = useAuth();
const location = useLocation();
return (
auth?.user
? <Outlet />
: <Navigate to='/' state={{ from: location }} replace />
&& setRequireLoginAlert(true)
);
};
export default RequireAuth;
My AuthContext.js
const AuthContext = createContext({});
export const AuthProvider = ({ children }) => {
const [auth, setAuth] = useState({});
const [requireLoginAlert, setRequireLoginAlert] = useState(false);
return (
<AuthContext.Provider value={{ auth, setAuth, requireLoginAlert, setRequireLoginAlert }}>
{children}
</AuthContext.Provider>
);
};
export default AuthContext;
I have try using useEffect the display the alert whenever the location has changed, but in this way the alert will keep popping out whenever I go to other pages, which is not ideal. I need a trigger to change the requireLoginAlert state after the user was redirected without error.
Feel free the drop a comment if you have any idea. Thanks a lot.
I think you were on the good way with the useEffect approach.
Indeed you have use the location in the dependencies array, but you also need to include a condition based on the current page so that the requireLoginAlert is not called every time
Have you tried something like the following piece of code ?
useEffect(
() => auth?.user && location === "/" && setRequireLoginAlert(true),
[auth?.user, location]
);

How can I make useEffect not return the initial value? I keep getting empty array when I'm trying to pass an array of objects into a component

I'm trying to pass in an array of registered users into a component, however I can't because it always renders the initial empty array before actually rendering the correct content. I've tried using useRef and it still does not work.
const Home = () => {
const nav = useNavigate()
const [userList, setUserList] = useState([]);
const [loggedInUser, setLoggedInUser] = useState({});
const [currentChat, setCurrentChat] = useState(undefined);
const [showMenu, setShowMenu] = useState(false);
useEffect(() => {
const setLoggedIn = async() => {
if (!localStorage.getItem('loggedInUser')) {
nav('/');
} else {
setLoggedInUser(await JSON.parse(localStorage.getItem('loggedInUser')))
}
}
setLoggedIn().catch(console.error);
}, [])
useEffect(() => {
const fetchUsers = async () => {
const data = await axios.get(`${allUsersRoute}/${loggedInUser._id}`);
setUserList(data.data);
}
fetchUsers().catch(console.error);
}, [loggedInUser._id])
console.log(userList);
return (
<div id='container'>
<div id='sidebar'>
<div>
<div id='home-header'>
<h1>DevsHelp</h1>
</div>
<Userlist props={userList}/>
</div>
</div>
)};
And here is the component I'm trying to render.
const Userlist = (props) => {
return (
<div>
<div id='home-header'>
<h1>DevsHelp</h1>
</div>
<div id='userlist'>
{props.map((prop) => {
{console.log(props.length)}
return (
<div className='user'>
<h3>{prop.username}</h3>
</div>
)
})}
</div>
</div>
)}
export default Userlist;
So basically, react returns .map is not a function, and I assume it's because the array goes in empty. I'm fairly new to React, so if anyone could help me, I would greatly appreciate it. Thanks!
The problem is that you are mapping over the props object, not the userList.
Try to do the following:
const Userlist = (props) => {
return (
<div>
<div id='home-header'>
<h1>DevsHelp</h1>
</div>
<div id='userlist'>
// use props.users.map instead of props.map
{props.users.map((user) => {
return (
<div className='user'>
<h3>{user.username}</h3>
</div>
)
})}
</div>
</div>
)}
export default Userlist;
and at Home component change the props of UserList to users, just to avoid confusion
<Userlist users={userList}/>
I wouldn't name props for a component "props", really:
<Userlist props={userList}/>
If you really want to, then at least inside Userlist, you would need to refer to the props object:
props.props.map...
Name your props to something that make sense to you, like for example "users". Then call props.users.map(user => {...})
A React component can take many props. When you want to access them inside the component, you need to do so by name. In your case, you defined Userlist like this:
function Userlist(props){...}
In this case, all props would have to be accessed via the props object. You defined a props value inside this object when you called <Userlist props={userList]} />
Personally, I always destructure props when I define a new component, like this:
function Userlist({users}) {...}
As a side note, your code would have worked if you had destructured the props object: function Userlist({props}) {...} This would be the smallest change you could do to make the code work, I guess. But again, I would not use "props" as a name for a prop.

Context API, Cannot retreive the data I am setting in seperate component

Working on a little project of mine but ran into an issue while working with the so called Context API. First time doing it as well.
The issue I am having is that I am unable to console.log the data I am setting. Now I am trying to make a "Add To Cart" button, and whenever the button is pressed. An object containing things such as name, price, color etc will be sent over to my ShoppingCart component.
I am able to set the value, whenever I press the button. And it appears in the console.log. Though, I am unable to console.log the value from ShoppingCart.
My files look like this:
./Contexts
./AddToCartContext.js
./Components
./Product
./ProductContent.js // Here the "add to cart" button is located.
./ShoppingCart
./ShoppingCart.js // Here I need the data from ProductContent.js
App.js
Here is my App.js code:
const [cartItems, setCartItems] = useState({});
console.log(cartItems); // The data is being logged here perfectly.
return (
<AddToCartContext.Provider value={{ cartItems, setCartItems }}>
<ThemeProvider theme={themeMode}>
{/* GlobalStyles skapas i ./themes.js */}
<GlobalStyles />
<Router>
<Route exact path="/cart">
<ShoppingCart theme={theme} toggleTheme={toggleTheme} />
</Route>
<Route exact path="/category/:type/:id/:productid">
<FetchAPI />
</Route>
// ...
</Router>
</ThemeProvider>
</AddToCartContext.Provider>
);
Here is my shoppingcart.js code:
import { AddToCartContext } from "../../Contexts/AddToCartContext";
const ShoppingCart = (props) => {
const { cartItems } = useContext(AddToCartContext);
console.log(cartItems); // This always result in an empty Object.
return (
<Fragment>
<TopNavigation />
<BottomNavigation theme={props.theme} toggleTheme={props.toggleTheme} />
<Cart />
<Footer />
</Fragment>
);
};
And here is the code in ProductContent.js (shorten as its pretty long):
import { AddToCartContext } from "../../Contexts/AddToCartContext";
const ProductContent = (props) => {
const { setCartItems } = useContext(AddToCartContext);
return (
<button
type="button"
onClick={() =>
setCartItems({ // This returns the object to App.js, but not Shoppingcart.js
name: props.name,
price: props.price,
color: props.mainImg?.colour,
img: props.mainImg?.url,
id: params.productid,
})
}
className={classes.add_to_cart}
>
<Cart />
add to cart
</button>
);
}
As mentioned, it's the first time I work with Context API. Am I missing anything?
EDIT:
Did some testing, and if I set the initial value on const [cartItems, setCartItems] = useState({}); to for example "test", then it is being rendered inside of the Cart page.
Nothing is logging inside of the cart upon the button press. But whenever the initial value is set to "test" it is loaded instantly.
Here is the code for creating the context (AddToCartContext.js):
import { createContext } from "react";
export const AddToCartContext = createContext({});
try without destructuring like this below
const cartItems = useContext(AddToCartContext);
console.log(cartItems);
and check if there is anything in that object

React router does not work correctly when used with react hooks?

I am working on a react application in which I have 3 component, AdminShopRoutes, AdminShop and Products.
The AdminShopRoutes Component:
const AdminShopRoutes = () => {
return (
<Router>
<Header>
<AdminShop exact path='/admin/shop' component={Dashboard} />
<AdminShop exact path='/admin/shop/customers' component={Customers} />
<AdminShop exact path='/admin/shop/products' component={Products} />
</Header>
</Router>)
}
The AdminShop Component
const AdminShop = ({ component: Component, ...rest }) => {
return (
<Route {...rest} render={(props) => (
true
? <Component {...props} />
: null
)} />
)
}
And finally the Product Component
const Products = (props) => {
useEffect(() => props.getProducts(), [])
const { products, loading } = props
return ( ... )
}
export default connect(mapStateToProps, { getProducts })(Products)
The links present in the other components beside these work i.e the url is changed but the page is blank as soon as the url changes for every route. If I then refresh the page, it renders fine but only on REFRESH. Also if I omit the route rendering the Products component all other routes work fine. Is there some other method of using hooks when working with react router because it is something to with Products component. Any help would be appreciated.
This is the warning I get when i render the product page
Arrow functions without {} will return the value of their one statement.
Arrow function with {} require an explicit return statement to return a value.
So the result of props.getProducts() is being returned from your effect.
However, useEffect() restricts the return value to only be a cleanup function for that effect. A non function return value is considered to be an error by React.
To fix this, just add {} to your arrow function so that it does not return a value:
useEffect(() => {
props.getProducts()
}, [])
do this
useEffect(() => {
props.getProducts()
}, [])
so that props.getProducts() doesn't get returned
Your error shows this has nothing to do with routing, but your useEffect is returning something when it shouldn't.
The error is pretty verbose and clear.

Categories

Resources