useEffect(()=>{
// console.log(`Ahanda items`,items)
setItems(JSON.parse(localStorage.getItem(`baskets`)) )
},[])
useEffect(()=>{
localStorage.setItem(`baskets`,`${JSON.stringify(items)}`)
},[items])
Hello i have classic async problem have.
problem is
there is basket for eccommerce shopping site basket simple one i want to
when site is refreshing if localstorage inside have any list of product item pull
and setItem
but the problem is the other items Useeffect works to. So if i add timeout and do like that :
useEffect(()=>{
// console.log(`Ahanda items`,items)
setItems(JSON.parse(localStorage.getItem(`baskets`)) )
},[])
useEffect(()=>{
setTimeout(() => {
localStorage.setItem(`baskets`,`${JSON.stringify(items)}`)
}, 200);
},[items])
but the problem is not solved because some times i can be use api's
here is other explanation https://www.youtube.com/watch?v=-mTvd1O-3nM
I might get the question wrong. But please correct me.
Are you looking for something like this ????
const[items, setItems] = useState([])
function handleFetchItems(){
if(localStorage.baskets){ //call this only if I have items
setItems(state =>[...state,...JSON.parse(localStorage.getItem(`baskets`))]
}
fetch(`url`).then((response) => { //async operation
setItems(state =>[...state,...response])
}).catch(() => {})
}
useEffect(useCallback(() => {handleFetchItems()},[]))
I already face This problem, and solve it by making a custom hook :
by the way, This custom hook was created by Kent creator of Remix , you can check it here : https://github.com/kentcdodds/react-hooks
usage:
const [name, setName] = useLocalStorageState({
defaultValue: 'default value',
key: 'name',
})
name is the value stored under the key 'name' in localStorage
setName is the setter of the new data in key 'name' in localStorage
Related
I am doing a fetch request in useEffect hook. It is how it looks like.
useEffect(() => {
// clear prev state
setUniqueValues([]);
setLoading(true);
// get unique values and set field values;
jwtAxios
.post(`${baseURL}/support/get_unique_values`, {
field: fieldName,
catalog_id: catalogID,
page,
category_values: props?.initialValues?.schema?.category,
})
.then((data) => {
setUniqueValues(data.data.unique_values);
setLoading(false);
setError(false);
})
.catch((err) => {
console.log(err);
setError(err);
});
}, [catalogID, fieldName, page]);
In the post request I want to send a value (category_values) if it is exists, if it comes with props. so I check with ?.
But if I don't put it in dependency array it complains:
warning React Hook useEffect has a missing dependency: 'props?.initialValues?.schema?.category'. Either include it or remove the dependency array react-hooks/exhaustive-deps
If I add it like [catalogID, fieldName, page,props?.initialValues?.schema?.category ]
It complains like :
Line 100:8: React Hook useEffect has a missing dependency: 'props.initialValues.schema.category'. Either include it or remove the dependency array react-hooks/exhaustive-deps
Line 100:37: React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked react-hooks/exhaustive-deps
If I don't put ?'s when props are not passed, it is collapsing because it doesn't find props.
How do I solve it? Thanks.
you can compute the category just before calling useEffect
const category = props?.initialValues?.schema?.category;
useEffect(() => {
setUniqueValues([]);
setLoading(true);
jwtAxios
.post(`${baseURL}/support/get_unique_values`, {
field: fieldName,
catalog_id: catalogID,
page,
category_values: category,
})
...
}, [catalogID, fieldName, page, category]);
I have this page which shows a single post and I have a like button. if the post is liked, when the user clicks the button, it changes its state to unlike button, but if the post is not liked, then the like is getting registered and the id is getting pushed on to the array, but the button state is not getting updated and I have to reload the page to see the page. Can someone tell me how to resolve this issue?
This is the code:
const [liked, setLiked] = useState(false)
const [data, setData] = useState([]);
function likePosts(post, user) {
post.likes.push({ id: user });
setData(post);
axiosInstance.post('api/posts/' + post.slug + '/like/');
window.location.reload()
}
function unlikePosts(post, user) {
console.log('unliked the post');
data.likes = data.likes.filter(x => x.id !== user);
setData(data);
return (
axiosInstance.delete('api/posts/' + post.slug + '/like/')
)
}
For the button:
{data.likes && data.likes.find(x => x.id === user) ?
(<FavoriteRoundedIcon style={{ color: "red" }}
onClick={() => {
unlikePosts(data, user)
setLiked(() => liked === false)
}
}
/>)
: (<FavoriteBorderRoundedIcon
onClick={() => {
likePosts(data, user)
setLiked(() => liked === true)
}
}
/>)
}
Thanks and please do ask if more details are needed.
As #iz_ pointed out in the comments, your main problem is that you are directly mutating state rather than calling a setState function.
I'm renaming data to post for clarity since you have said that this is an object representing the data for one post.
const [post, setPost] = useState(initialPost);
You don't need to use liked as a state because we can already access this information from the post data by seeing if our user is in the post.likes array or not. This allows us to have a "single source of truth" and we only need to make updates in one place.
const isLiked = post.likes.some((like) => like.id === user.id);
I'm confused about the likes array. It seems like an array of objects which are just {id: number}, in which case you should just have an array of ids of the users who liked the post. But maybe there are other properties in the object (like a username or timestamp).
When designing a component for something complex like a blog post, you want to break out little pieces that you can use in other places of your app. We can define a LikeButton that shows our heart. This is a "presentation" component that doesn't handle any logic. All it needs to know is whether the post isLiked and what to do onClick.
export const LikeButton = ({ isLiked, onClick }) => {
const Icon = isLiked ? FavoriteRoundedIcon: FavoriteBorderRoundedIcon;
return (
<Icon
style={{ color: isLiked ? "red" : "gray" }}
onClick={onClick}
/>
);
};
A lot of our logic regarding liking and unliking could potentially be broken out into some sort of usePostLike hook, but I haven't fully optimized this because I don't know what your API is doing and how we should respond to the response that we get.
When a user clicks the like button we want the changes to be reflected in the UI immediately, so we call setPost and add or remove the current user from the likes array. We have to set the state with a new object, so we copy all of the post properties that are not changing with the spread operator ...post and then override the likes property with an edited version. filter() and concat() are both safe array functions which return a new copy of the array.
We also need to call the API to post the changes. You are using the same url in both the "like" and "unlike" scenarios, so instead of calling axios.post and axios.delete, we can call the generalized function axios.request and pass the method name 'post' or 'delete' as an argument to the config object. [axios docs] We could probably combine our two setPost calls in a similar way and change likePost() and unlikePost() into one toggleLikePost() function. But for now, here's what I've got:
export const Post = ({ initialPost, user }) => {
const [post, setPost] = useState(initialPost);
const isLiked = post.likes.some((like) => like.id === user.id);
function likePost() {
console.log("liked the post");
// immediately update local state to reflect changes
setPost({
...post,
likes: post.likes.concat({ id: user.id })
});
// push changes to API
apiUpdateLike("post");
}
function unlikePost() {
console.log("unliked the post");
// immediately update local state to reflect changes
setPost({
...post,
likes: post.likes.filter((like) => like.id !== user.id)
});
// push changes to API
apiUpdateLike("delete");
}
// generalize like and unlike actions by passing method name 'post' or 'delete'
async function apiUpdateLike(method) {
try {
// send request to API
await axiosInstance.request("api/posts/" + post.slug + "/like/", { method });
// handle API response somehow, but not with window.location.reload()
} catch (e) {
console.log(e);
}
}
function onClickLike() {
if (isLiked) {
unlikePost();
} else {
likePost();
}
}
return (
<div>
<h2>{post.title}</h2>
<div>{post.likes.length} Likes</div>
<LikeButton onClick={onClickLike} isLiked={isLiked} />
</div>
);
};
CodeSandbox Link
I try to navigate from home screen to detail screen using navigation.navigate(). When the first time i click button to navigate it works fine, but after that when i go back from detail to home and click another item to see the detail, the params on detail screen is not changed (its still the params of the 1st click). Here my code :
<TouchableNativeFeedback
key={index}
onPress={() => {
navigation.navigate('PenghuniScreen', {
screen: 'DetailPenghuni',
params: {
penghuni: item,
},
});
}}></TouchableNativeFeedback>;
item is object from map loop . i try to console log using react navigation usefocuseffect and the params not changin after 1st click.
useFocusEffect(
React.useCallback(() => {
console.log('My params:', route.params.penghuni);
setPenghuni(route.params.penghuni);
return () => {
// source.cancel('Api Canceled');
console.log('tutup detail penghuni');
};
}, []),
);
anyone got solution for this?
You forgot to add the route dependency to your useCallback, so it is getting the old value.
Try adding [route]:
React.useCallback(() => {
console.log('My params:', route.params.penghuni);
setPenghuni(route.params.penghuni);
return () => {
// source.cancel('Api Canceled');
console.log('tutup detail penghuni');
};
}, [route]),
I suggest you adding the eslint for hooks to your project, to detect missing dependecies.
Observation: Why don't you pass these values though the navigation params instead of using useFocusEffect?
navigation.navigate('DetailPenghuni', { penghuni: item1 }) // item1 press
navigation.navigate('DetailPenghuni', { penghuni: item2 }) // item2 press
If you need to cancel something you can always use the useEffect clean callback called automatically when you close a screen.
You can use route.params.penghuni directly instead of setting setPenghuni(route.params.penghuni)
useEffect(() => {
console.log('My params:', route.params.penghuni);
// setPenghuni(route.params.penghuni);
return () => {
// source.cancel('Api Canceled');
console.log('tutup detail penghuni');
};
}, [route.params?.penghuni]),
I'm trying to render my Guild component with data from Firestore. I put the data from Firestore into my state as an array, then when I call the component and try to render it, nothing shows. I want to believe I'm doing something very wrong here (haven't been working with React for very long), but I'm not getting any errors or warnings, so I'm not sure exactly what's happening.
Guilds.js
<Col>
<Card>
<CardBody>
<CardTitle className={this.props.guildFaction}>{this.props.guildName}</CardTitle>
<CardSubtitle>{this.props.guildServer}</CardSubtitle>
<CardText>{this.props.guildDesc}</CardText>
</CardBody>
</Card>
</Col>
Render function
renderCards() {
var guildComp = this.state.guilds.map(guild => {
console.log(guild)
return <Guilds
key={guild.id}
guildFaction={guild.guildFaction}
guildServer={guild.guildServer}
guildName={guild.guildName}
guildDesc={guild.guildDesc} />
})
return <CardDeck>{guildComp}</CardDeck>
}
Fetching Firestore Data
guildInfo() {
Fire.firestore().collection('guilds')
.get().then(snapshot => {
snapshot.forEach(doc => {
this.setState({
guilds: [{
id: doc.id,
guildDesc: doc.data().guildDesc,
guildFaction: doc.data().guildFaction,
guildName: doc.data().guildName,
guildRegion: doc.data().guildRegion,
guildServer: doc.data().guildServer
}]
})
console.log(doc.data().guildName)
})
})
}
UPDATE: solved, fix is in the render function.
Well, you using state "guilds" but you update state "posts" or I miss something?
I see few things here:
your component is Guild.js, but you are rendering <Guilds />
You are setting state to posts, but using this.state.guilds to render the components
You are overriding that piece of state each time to the last object in the snapshot, with the way you are mapping the Firestore data
you are setting the ids in the list wrong using doc.id instead of doc.data().id
You aren't mapping guilds to render. guilds is an array of guild objects, so you should do something like guilds.map(guild => { return <Guild /> }
These are few things to fix, and then try to console.log(this.state.guilds) before rendering and see if you get the right data
I think your issue is that because setState is async, by the time it actually sets the state doc is no longer defined. Try creating the array first, then call setState outside of the loop ie:
guildInfo() {
Fire.firestore().collection('guilds')
.get().then(snapshot => {
let guilds = []
snapshot.forEach(doc => {
guilds.push({
id: doc.id,
guildDesc: doc.data().guildDesc,
guildFaction: doc.data().guildFaction,
guildName: doc.data().guildName,
guildRegion: doc.data().guildRegion,
guildServer: doc.data().guildServer
});
})
this.setState({guilds});
})
}
Try to use a map function, and in the callback function of the setState, try to console log your state after the update:
guildInfo() {
Fire.firestore().collection('guilds')
.get()
.then(snapshot => {
const guilds = snapshot.map(doc => {
return {
id: doc.id,
guildDesc: doc.data().guildDesc,
guildFaction: doc.data().guildFaction,
guildName: doc.data().guildName,
guildRegion: doc.data().guildRegion,
guildServer: doc.data().guildServer
};
this.setState({guilds}, () => console.log(this.state))
})
})
})
}
If in the console log there's a little [i] symbol near your state, it means that the state is not ready, and therefore it's am async issue. Replacing the forEach with the map function may already help though.
I cannot figure out how can I solve the following problem.
There is an object type:
Box {
Fruit[n]: {
Kinds[n]: {
id: string;
name: string;
}
}
}
I got the box of fuits from an API call as an Observable (Angular2) [Fruit[]] then I want to populate its "navigation property" with another API call what gives back an observable as well like:
Box.foreach(fruits =>
fruits.foreach(f =>
f.kinds.foreach(k =>
k.name = kindservice.getKindName(k.id) // <- observer
)))
How can I do it with RxJs?
I tried many ways, there are many mapper but I could not figure out yet.
I used the Observable.from(..) as well but there was no luck.
Thank you
Your most inner loop has 2 problems:
You assign an observer to a value.
Your observer stays cold.
Try changing it to:
Box.foreach(fruits =>
fruits.foreach(f =>
f.kinds.foreach(k =>
kindservice.getKindName(k.id).subscribe(name => k.name = name)
)))