LocalStorage doesn't stay updated on page refresh in MERN Application - node.js

LocalStorage somehow doesn't want to stay updated on refresh page. I'm not sure why is that happening, and where is my mistake. The backend is updating fine but the localStorage doesn't stay updated.
Here is where I'm updating localStorage:
const UserContextProvider = (props) => {
const [user, setUser] = useState({})
const [cart, setCart] = useState(localStorage.getItem("cart") || [])
useEffect(() => {
fetch(`${API}/auth/user`, {
method: 'GET',
withCredentials: true,
credentials: 'include'
})
.then (response => response.json())
.then (response => {
setUser(response.user)
})
.catch (error => {
console.error (error);
});
}, [setUser])
useEffect(() => {
localStorage.setItem("cart", JSON.stringify(user.cart))
}, [setUser])
const addToCart = (user, product) => {
fetch(`${API}/cart/usercart`, {
method:"POST",
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify([user._id, product])
})
.then(() => setUser({...user, cart: [...user.cart, product]}))
.catch(error => console.log(error))
}
return (
<UserProvider value={[user, setUser, addToCart, cart]}>
{props.children}
</UserProvider>
)
}
export default UserContextProvider;
And here is the component where I'm trying to use it:
const Cart = props => {
const [user, setUser, cart] = useContext(UserContext)
...
{cart?.length === 0 ? <></> :
<>
{cart?.map(product => {
return(...)

The following code only runs once when the component mounts:
useEffect(() => {
setCart(JSON.parse(localStorage.getItem("cart")) || [])
}, []);
In order to render the latest value you'll have to depend on a prop or something that updates when cart updates:
useEffect(() => {
setCart(JSON.parse(localStorage.getItem("cart")) || [])
}, [props.something]);
Based on your implementation, I'd recommend having your contextProvider provide this data instead of component itself retrieving it from localStorage on each render. You only need the localStorage value when the app starts.

Related

POST http://localhost:8080/todo net::ERR_ABORTED 400 (Bad Request), how do I fix this?

I'm creating a to-do list that uses authentication and the todo has to connect to the MongoDB server. The bad request is because of syntax, probably the to-do list sending back from client-side to server-side. How do I fix this error?
This is the page the error is on: TodoPage.js
import React, { useState, useEffect } from 'react';
//components
import ToDoList from '../Components/ToDoList';
import ToDoForm from '../Components/ToDoForm';
function ToDoPage() {
const [toDoList, setToDoList] = useState([]);
const url = 'http://localhost:8080/todo';
useEffect(function effectFunction() {
fetch(url, {
method: 'POST',
mode: 'no-cors',
})
.then((response) => response.json())
.then(({ data: toDoList }) => {
setToDoList(toDoList);
});
}, []);
const handleToggle = (id) => {
let mapped = toDoList.map((task) => {
return task.id === Number(id)
? { ...task, complete: !task.complete }
: { ...task };
});
setToDoList(mapped);
};
const handleFilter = () => {
let filtered = toDoList.filter((task) => {
return !task.complete;
});
setToDoList(filtered);
};
const addTask = (userInput) => {
let copy = [...toDoList];
copy = [
...copy,
{ id: toDoList.length + 1, task: userInput, complete: false },
];
setToDoList(copy);
};
return (
<div className='App'>
<ToDoList
toDoList={toDoList}
handleToggle={handleToggle}
handleFilter={handleFilter}
/>
<ToDoForm addTask={addTask} />
</div>
);
}
export default ToDoPage;
The console says it is this block of code that is giving trouble.
useEffect(function effectFunction() {
fetch(url, {
method: 'POST',
mode: 'no-cors',
})
.then((response) => response.json())
.then(({ data: toDoList }) => {
setToDoList(toDoList);
});
}, []);

How to update UI after decreasing qtty from backend in react JS?

When I reload the page then quantity is being updated but until page reload quantity does not update on UI. On the other hand, my database decreasing the quantity.
const StockUpdate = () => {
const { id } = useParams();
const [stockUpdate, setStockUpdate] = useState({});
useEffect(() => {
const getItemById = async () => {
const url = `http://localhost:8000/inventory/${id}`
const { data } = await axios.get(url);
setStockUpdate(data);
}
getItemById();
}, [id]);
const handleDeliveryButton = () => {
console.log(stockUpdate)
const item = stockUpdate;
const url = `http://localhost:8000/inventory/${id}`;
fetch(url, {
method: 'PUT',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(item),
})
.then(res => res.json())
.then(data => {
console.log(data)
});
toast('Stock quantity updated after delivery !!!!');
};

Trying to load content with useEffect and context

I am trying to show information of a state into a component. I am using the context to load information from different origin.
const router = useRouter()
const [sidebarOpen, setSidebarOpen] = useState(false)
const [loadUser, setLoadUser] = useState({})
const { state, dispatch } = useContext(Context)
const { user } = state
useEffect(() => {
if (user == null) {
router.push("/auth/login")
}
setLoadUser(user)
}, [user])
That code is inside a dashboard component. The idea is to get the user information into the state to show it on the dashboard. The problem is that the useEffect is executed at the same time that the content is rendered, therefore it does not have time to load the information, and the variables are null for me.
Here is an image of how the loadState variable behaves once inside the render.
I am using nextjs by the way.
I am passing the context with a provider to the App. And wrapped it.
// initial state
const initialState = {
user: null
};
// Create context
const Context = createContext()
// root reducer
const reducer = (state, action) => {
const { type, payload } = action;
switch (type) {
case "LOGIN":
return { ...state, user: payload};
case "LOGOUT":
return { ...state, user: null };
default:
return state;
}
}
// context provider
const Provider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
const router = useRouter();
useEffect(() => {
dispatch({
type: "LOGIN",
payload: JSON.parse(window.localStorage.getItem("user"))
})
}, [])
axios.interceptors.response.use(
function (response) {
return response
},
function (error) {
let res = error.response;
console.log(error)
if (res.data.status === 401 && res.config && !res.config.__isRetryRequest) {
return new Promise((resolve, reject) => {
axios.get(`${process.env.NEXT_PUBLIC_API}/auth/logout`)
.then(res => {
dispatch({ type: "LOGOUT" })
window.localStorage.removeItem("user")
router.push('/auth/login')
})
.catch(error => {
reject(error)
})
})
}
return Promise.reject(error);
}
)
useEffect(() => {
const getCsrfToken = async () => {
const { data } = await axios.get(`${process.env.NEXT_PUBLIC_API}/csrf-token`)
axios.defaults.headers["X-CSRF-Token"] = data.csrfToken
}
getCsrfToken()
}, [])
return (
<Context.Provider value={{ state, dispatch }}>
{children}
</Context.Provider>
)
}
export { Context, Provider }
Add dependecy array. That shoud be quick fix.
useEffect(() => {
if (user == null) {
router.push("/auth/login")
}
setLoadUser(user)
}, [state]) // dependecy array looks at state changes
You can read more about dependecy array on the official react documentation: https://reactjs.org/docs/hooks-effect.html
proper fix:
whenever you are using api calls in your app, those are asyncrhonus. You should use some loading state.
for example
const Context = () => {
const [loading, setLoading] = useState(true)
const [user, setUser] = useState(true)
fetch('api/user').than(r=> setLoading(false);setUser(r.user))
// rest of the context code
}
and in component itself:
const state = useContext(Context)
useEffect(() => {
state.loading ? null : setLoadUser(user)
}, [state]) //

How to update state in Context using Hooks in MERN Stack Application

I'm trying to find a way to update my "user" state, but I'm stuck here for 3 days already, I need some help.
Here is my user context:
import React, {useEffect, useState} from 'react';
export const UserContext = React.createContext({})
const UserProvider = UserContext.Provider;
const UserContextProvider = (props) => {
const [user, setUser] = useState({})
useEffect(() => {
fetch(`${API}/auth/user`, {
method: 'GET',
withCredentials: true,
credentials: 'include'
})
.then (response => response.json())
.then (response => {
setUser(response.user)
})
.catch (error => {
console.error (error);
});
}, [setUser])
console.log(user)
return (
<UserProvider value={{user, setUser}}>
{props.children}
</UserProvider>
)
}
export default UserContextProvider;
Here is where I'm trying to update the user. In my case I'm trying to push an object in user.cart array, cuz everything on the back-end is fine, but in the front-end the state is not updating:
First I'm using the UserContext:
const Products = () => {
const {user, setUser} = useContext(UserContext) ...
And then here I'm trying to update the user state, BUT when I click the button it logged me out:
<button className="addTo--Cart--Button--Container">
<FaShoppingCart onClick={() => {addToCart(user._id, product); setUser(oldState => oldState.cart.push(product))}}/>
</button>
After this logged me out, the console.log(user) which is in UserContextProvider function log only the user.cart updated lenght.
AND one more:
How to remove item from context:
Here is my remove function:
const removeFromContextCart = (id) => {
console.log(id)
const updatedCart = user.cart.filter((item) => item.id !== id);
setUser(oldState => ({
...oldState,
cart: [
...oldState.cart,
updatedCart
]
}))
}
And my button:
<button className="remove--Button" onClick={() => {removeFromCart(user._id, product); setUser(removeFromContextCart(product._id))}}> REMOVE</button>
Try updating the user state in this way
setUser(oldState => ({
...oldState,
cart: [
...oldState.cart,
product
]
}))

I am trying to fetch All text(tweet like a post) from database. REACT NATIVE

I am trying to fetch All text(tweet like a post) from database.but its showing error TypeError: undefined is not a function (near '...posts.map...')
below is my code:
import React, {useEffect, useState} from 'react';
import {ActivityIndicator, Text, View, FlatList} from 'react-native';
import stylesheet from '../src/styles/HomeStyle';
import AsyncStorage from '#react-native-community/async-storage';
function FeedScreen() {
// const [posts, setPosts] = useState('loading');
const [posts, setPosts] = useState({
text: '',
id: '',
});
const Boiler = async () => {
const token = await AsyncStorage.getItem('token');
fetch('http://192.168.1.5:3000/post', {
headers: new Headers({
Authorization: 'Bearer ' + token,
}),
})
.then(res => res.json())
.then(res => {
setPosts(res.posts);
})
.catch(err => console.log(err));
};
useEffect(() => {
Boiler();
}, []);
return (
<View style={stylesheet.container}>
{posts.map(post => (
<Text key={post.id}>{post.text}</Text>
))}
</View>
);
}
export default FeedScreen;
The problem is the initial value for posts is a post and not an array of posts. I also renamed Boiler as initial uppercase should be reserved for components or classes, and moved it inside the useEffect:
const FeedScreen = () => {
const [posts, setPosts] = useState([]); // Here was the issue
useEffect(() => {
const boiler = async () => {
const token = await AsyncStorage.getItem("token");
fetch("http://192.168.1.5:3000/post", {
headers: new Headers({
Authorization: "Bearer " + token
})
})
.then(res => res.json())
.then(res => {
setPosts(res.posts);
})
.catch(err => console.log(err));
};
boiler();
}, []);
return (
<View style={stylesheet.container}>
{posts.map(post => (
<Text key={post.id}>{post.text}</Text>
))}
</View>
);
};
export default FeedScreen;

Resources