modal update has delay to set new State in react hook - javascript

I have some problem with updating the editModal (react bootstrap modal).
after dispatching the action it works and I'm able to fetch single pet data as an object.
and redux dev tools confirms that
But modal doesn't open at first attempt and I need to click the (edit button: doesn't matter which one) again to show filled input data ,and each time I click the edit button for first pet or second pet it doesn't update modal and it shows data related to the previously clicked edit
and as you see I clicked the second edit and data has fetched as redux dev tools shows correctly but Modal still shows first pet details
thanks for your help :)
here's my code
const PetScreen = () => {
const dispatch = useDispatch()
//part of state that comes from store.js
const petDetailByUser = useSelector((state) => state.petDetailByUser)
//when action dispatch petDetails fills with single pet Details ex:_id,editPetName,...
const { petDetailLoading, petDetails } = petDetailByUser
const [editPetName, setEditPetName] = useState('')
//edit button dispatch getPetDetail and get pet data as an object
const editButtonHandler = (id) => {
dispatch(getPetDetails(id))
if (petDetails) {
setEditPetName(petDetails.editPetName)
//shows edit modal
handleEditShow()
}
}
//this field is inside react modal body
<Form.Control
type='text'
placeholder='Pet Name'
name='editPetName'
value={editPetName}
onChange={(e) => setEditPetName(e.target.value)}
>
</Form.Control>

Just after dispatch(getPetDetails(id)), the editPetName variable has not been refreshed yet so it contains the name of the previous pet.
You can fix it by using an effect to update editPetName when petDetails is updated.
const petDetailByUser = useSelector((state) => state.petDetailByUser)
const { petDetailLoading, petDetails } = petDetailByUser;
const [editPetName, setEditPetName] = useState('');
useEffect(() => {
if (petDetails) {
setEditPetName(petDetails.editPetName);
handleEditShow();
}
}, [petDetails]);
const editButtonHandler = (id) => dispatch(getPetDetails(id));

Related

Unable to access state - React.js working with Websocket

This is my first post here, so nice to meet you all. I have recently started my adventure with React, so my question probably will not be very challenging (it could also be very basic stuff), however I got stuck with this problem for a while.
So I am working with WebSocket that when connected, sends one-time message with all products currently in stock. Then, every few seconds, it sends object with update of product stock changes.
I managed to update state with first recieved message, but then, when I try to access state in a function handleData, state is empty array. This is happening despite the fact that the data rendered on the page using state is still visible, state is visible in Firefox Developer Edition React DevTools, and the useEffect that is associated with the state change fires only once - at the start, so it doesn't change.
I want to be able to access data I put before in state, in function handleData, so that I can update the state with stock changes. It is interesting that when the websocket loads again, the "products" status variable is extended by the previous, unloaded products.
Here is code of my component:
import React, { useState, useEffect, useRef } from 'react';
import Product from './Product';
export default function ProductsList() {
const [products, setProducts] = useState([]);
const ws = useRef(null);
useEffect(() => {
ws.current = new WebSocket(<HERE IS WEBSOCKET URL BUT I CENSORED IT>);
ws.current.onmessage = (message) => handleData(message);
return () => ws.current.close();
},[]);
useEffect(() => {
console.log("Products useEffect:", products)
}, [products]) //prints fine here
const handleData = (message) => {
const data = JSON.parse(message.data);
switch (data.operation) {
case 'product.stock.decreased': {
console.log("Products Switch:", products) // prints empty array here
break;
}
default: {
setProducts(prevState => [...prevState, ...data]) // here state updates
break;
}
}
}
return (
<div className="ProductsList">
{products.map(p => <Product key={p.productId} productId={p.productId} name={p.name} price={p.price} stock={p.stock} />)}
</div>
);
}
Many thanks in advance for your help.

Variable storage update across react component

This is a next/react project.
folder structure:
components > Navbar.js
pages > index.js (/ route)(includes Navbar)
> submitCollection.js (/submitCollection)(includes Navbar)
I am trying to have the user submit a specific string as an input and i store it inside the account variable.
Navbar.js
const Navbar = ({}) => {
const [account,setAccount] = useState()
const handleClick = () => {
setAccount(randomNumberThatIHaveGenerated)
}
...
return (
<Link href="/">home</Link>
<Link href="/submitCollection">submit collection</Link>
...
<button onClick={handleClick} >press to set account</button>
...
{account?(<p>{account}</p>):(<p>u need to set an accout</p>)}
)
}
when i visit home using the navbar link, the account is again set to undefineed and i need to press the button again in order to set it. How can i make the string remain set. like persist on the navbar
useState is not persistent, it is bound to its component, in order to make it persist, you have to use localStorage
const [account,_setAccount] = useState();
const setAccount = (val) => {
_setAccount(val);
localStorage.setItem('account', val);
}
useEffect(() => {
const storedAccount = localStorage.getItem('account');
if (storedAccount) _setAccount(storedAccount);
}, [])
const handleClick = () => {
setAccount(randomNumberThatIHaveGenerated)
}
useEffect is called when the component renders, check for stored account and displays it.
And notice how we reimplement setAccount, so that everytime it is called, we update the localStorage.
You can also create a custom hook with this logic, so the component would look cleaner. Or even better, use something like use-state-persist
You can solve this problem using localstorage and useEffect
Adding this piece of code to your work will do the trick
const [account,setAccount] = useState(localStorage.getItem('account') ?localStorage.getItem('account') : null)
useEffect(()=>{
localstorage.setItem(account)
},[account])
For example
const [account,setAccount] = useState(localStorage.getItem('account') ?localStorage.getItem('account') : null)
useEffect(()=>{
localStorage.setItem('account',account)
},[account])
const handleClick = () => {
setAccount(randomNumberThatIHaveGenerated)
}
Hope it helped

ReactJS not updating state google map autocomplete fields

I have integrated google map auto-completed, it's working well, even i have console logged, every data is appearing as expected but the problem is, when i select, it doesn't update the state
Here you go for my component
import Autocomplete from "react-google-autocomplete";
const TestForm = () => {
const [profile, setProfile] = useState({});
const onPlaceSelected = (place) => {
console.log(place)
setProfile({...profile, "test": "test"})
console.log(profile, )
};
const handleProfileChange = (e) => {
setProfile({...profile, [e.target.name]: e.target.value})
};
return (
<>
<input onChange={handleProfileChange} type="text"></input>
<Autocomplete
apiKey="apikey"
defaultValue={formData.profile.location || ""}
onPlaceSelected={onPlaceSelected}
/>
</>
);
};
You may notice, i used two different method to update state, one for general input fields and another for autocomplete lib, but general input field state is updateing but autocomplete state is not updating, i have console logged the place, i see it is appearing, only problem is when i select a location, the selected location doesnt add my state, what is the issue? why it's behaving so weirds? can anyone help me?
You cant log the state right after you updated it, because it's async. Try logging the state change in useEffect hook
const [profile, setProfile] = useState({});
const onPlaceSelected = (place) => {
console.log(place)
setProfile({...profile, "test": "test"})
};
useEffect(() => { console.log(profile) }, [profile])

React parent component state updates but child does not re-render

I have a react app with many entries, each entry can have many tags.
It is a moderation app, so the entries are listed on a page and a user can click on an entry to moderate it (for example, to add or remove tags). Once clicked, the entry will show up in a modal.
Once the modal is open, a user can chain the entries with a 'next' button, so that the modal does not close. When the user clicks 'next', the next entry gets loaded into the modal.
In the modal, I have a react CreatableSelect component that takes the tag list of that loaded entry.
The issue is that when the user clicks 'next', the tags in the CreatableSelect don't update, it is still showing the tags of the first loaded entry.
Here is the code, transformed to make my issue hopefully clearer.
first, the component is loaded with an empty array of codes
second, the useEffect is triggered and populates the state with 2 dummy codes
Although when I console.log the state, it is correctly updated with the 2 dummy codes, the CreatableSelect still shows empty.
What I would like to understand is why the CreatableSelect does not rerender with the new state?
Thank you!
const SelectTags = ({ nextEntry, entry, topicId, updateEntry }) => {
const projectCodes = useSelector(state => state.project.codes);
const formatedCodes = projectCodes.map(code => ({value: code, label: code, isFixed: true}) );
const [selectedTags, setSelectedTags] = useState([]);
useEffect(() => {
const newTags = [{value: 'hello', label: 'hello'}, {value: 'world', label: 'world'}];
setSelectedTags([...newTags]);
}, [entry]);
const handleChange = newValue => setSelectedTags([...newValue]);
const setSubmittingFalse = () => setSubmitting(false);
return (
<CreatableSelect isMulti onChange={handleChange} options={formatedCodes} defaultValue={selectedTags} />
)
};
export default SelectTags;
Alright, switching the CreatableSelect props from defaultValue to value apparently solved that issue!
<CreatableSelect key={entry.id} isMulti onChange={handleChange} options={formatedCodes} value={tags} />

How to use useSelector on page refresh

I have following code inside my functional component.
const { id } = useParams();
const categories = useSelector((state) => {
const allCategories = state.allCategories;
return allCategories.filter(cat => cat.id === id);
});
const [code, setCode] = useState(categories.length === 1 ? categories[0].code : '');
However this code runs correctly only once. Then if I refresh the page, it doesn't work. Please help me to fix this issue. Also, if this is against best practices, please let me know the correct way to do this.
Actual use case is, there is a category table and user clicks on "edit category" and then I navigate to this component to display the current data (ie: category code). Since, the category table already has data, allCategories is filled
because the data is changed in some other place of the program its prolematic to use it as a default state value...
instead bind the value with useEffect()
const { id } = useParams();
const categories = useSelector((state) => {
const allCategories = state.allCategories;
return allCategories.filter(cat => cat.id === id);
});
const [code, setCode] = useState('');
useEffect(()=>{
if(categories.length === 1)
setCode(categories[0].code)
},[categories.length&&categories[0].?code])
NOTE: the code will be set like so whenever categories[0].code is changed...
You can use something like redux-persist to save the redux state to local storage or to another storage system

Categories

Resources