I'm building an app in React Native,
and I am trying to understand if I need to use useState & useContext to export a user info object,
or I can just get the object from the script, change it, and it will be changed to all the other scripts.
(or if there is a better way to do it so)
Thanks in advance :)
If you update data in your component, always use useState.
Furthermore if you store an array or an object into your state, it's a good practice to update the array/object, and then update your state with a spread operator (this will create a new array/object, and fired your useEffect and UI)
Example :
If you do a simple :
const [stateArray, setStateArray] = useState(["foo"])
stateArray[0] = "new foo"
All the components that consume the stateArray[0] will not update and continue to display a simple foo
This is caused becaused this update doesn't triggered a refresh as you're array isn't update, but one of it's component (not detect by React).
If you want an update, you need to do :
const [stateArray, setStateArray] = useState(["foo"])
stateArray[0] = "new foo"
setStateArray([...stateArray])
This will create a new array, and as such, triggered a new render of your component.
And the same logic applied for object, if you only update one of its value, there will be no update, you need to create a new object :
Example :
const [stateObject, setStateObject] = useState({foo: "foo"})
stateObject.foo = "new foo"
setStateObject({...stateObject})
Related
I have a question about using multiple mutation in a component.
For example, if I need to create and update the same component, how can I make them?
When I make like
const [createUser, {data}] = useMtation(CREATE_USER, {user}) - create user
const [updateUser, {data}] = useMtation(UPDATE_USER, {user}) - update user
Using those, I want to do like this -
If I click create, the first one is gonna work,
When I click the edit button, the button will show the update user and will work the second one.
Then, I have an error because I have two data.
Can I have some good examples for my question?
To resolve your error, you can rename the variable you are destructuring. As mentioned in the MDN docs here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment under the "Assigning to new variable names" section, you can do the following:
const [createUser, {data: createUserData}] = useMutation(CREATE_USER, {user}) // create user
const [updateUser, {data: updateUserData}] = useMutation(UPDATE_USER, {user}) // update user
To add a comment on the design of you component, it is not React good practice to bundle together distinct logic like create & update in the same component.
What you should ideally do is, have a separate component for your Create button where you have the only create mutation & a separate component for your Update button where you have the only the update mutation.
Is there any option to add zone without maping it and adding new object into state on first click?
https://codesandbox.io/s/charming-ellis-b7e8k?file=/src/App.js:319-324
My wanted behaviour is filling inputs and adding it into state on click. After added I will reset input to add more zones into state.
Someone told about using immer.js. Actually reading docs.. is there any other way?
Thank you
useState will require objects to be immutable to detect changes. So, array.push will usually not have the desired effect. Create a new one instead:
onClick={() =>
setZones([...zones, {
Server: "",
positionName: "",
positionIdentification: ""
}])
}
This will create a new array with the contents of the old plus the new content.
To avoid creating a new array every time, as you suggested immer can do that for you with the useImmer hook.
I have a large and deep object which I render using a React functional component (composed of child components). I also use Redux in the project which interacts with an API. To keep the app fast, I keep a local copy of the object in the component state and dispatch changes when occurred. The objects can also change in different parts of the app.
Each time the object changes in Redux its lastUpdated field is updated with the current time (epoch) by Redux. So instead of doing a deep (and expensive) diff on the whole object it is sufficient to compare the object id (in case the component has to display a different object) and the lastUpdated value (in case the object itself got changed).
This is what my functional component looks like:
interface Item {
id: string
lastUpdated: number
// more fields here
}
interface Props {
item : Item
}
export default function ItemPage(props: Props){
const [displayItem, setDisplayItem] = useState<Item>(props.item)
useEffect(() => {
if (props.item.id !== display.item.id && props.item.lastUpdated !== displayItem.lastUpdated){
setDisplayItem(props.item)
// ... some logic comes here
}
}, [props.item.id, props.item.lastUpdated])
return (
// ... render code here
)
}
This code cause the following error:
React Hook useEffect has missing dependencies: 'item.id' and
'item.lastUpdated'. Either include them or remove the dependency array
react-hooks/exhaustive-deps
I have disable the error with:
// eslint-disable-next-line react-hooks/exhaustive-deps
Questions:
Is it safe to to disable the error as I know the logic of my Redux (and use the more efficient diff)? Is there a better solution?
Is it safe to completely remove the useEffect array as I make the id/lastUpdated diff myself?
Note: I am not looking for the general "do not disable as it will come back to bite you ..." answer.
Looks like you need React.memo with second argument isEqual. Is equal works like shouldComponentUpdate. Read reactjs docs more about React.memo
UPDATE:
I've added codesandbox as example
I'm stuck in updating states , I'm using a function for generalising a Modal from a 3rd party framework now when I want to update the particular variable I'm stuck as dynamically its not updating ,i.e: Ex: setModalState(...oldstate,v1:{v2:false}} . Here ${v1} and ${v2} are already in old state and I passed to function as props but javascript is not taking insted taking v1,v2;
I have attached few photos for understanding
https://res.cloudinary.com/df2q7cryi/image/upload/v1615998821/error3_svxlcw.png
https://res.cloudinary.com/df2q7cryi/image/upload/v1615998764/eroor2_enjcbc.png
https://res.cloudinary.com/df2q7cryi/image/upload/v1615998762/eroor_bz5wwf.png
Solution is to use | setModalState({...modalstate,[section]:{[purpose]:false}})}
Reference : https://stackoverflow.com/questions/9398535/add-dynamic-key-value-pairs-to-javascript-array-or-hash-table/9398583#:~:text=You%20need%20to%20define%20an,square%20bracket%20and%20dot%20notation.
I've just started using Recoil on a new project and I'm not sure if there is a better way to accomplish this.
My app is an interface to basically edit a JSON file containing an array of objects. It reads the file in, groups the objects based on a specific property into tabs, and then a user can navigate the tabs, see the few hundred values per tab, make changes and then save the changes.
I'm using recoil because it allows me to access the state of each input from anywhere in my app, which makes saving much easier - in theory...
In order to generate State for each object in the JSON file, I've created an component that returns null and I map over the initial array, create the component, which creates Recoil state using an AtomFamily, and then also saves the ID to another piece of Recoil state so I can keep a list of everything.
Question 1 Is these a better way to do this? The null component doesn't feel right, but storing the whole array in a single piece of state causes a re-render of everything on every keypress.
To Save the data, I have a button which calls a function. That function just needs to get the ID's, loop through them, get the state of each one, and push them into an Array. I've done this with a Selector too, but the issue is that I can't call getRecoilValue from a function because of the Rules of Hooks - but if I make the value available to the parent component, it again slows everything right down.
Question 2 I'm pretty sure I'm missing the right way to think about storing state and using hooks, but I haven't found any samples for this particular use case - needing to generate the state up front, and then accessing it all again on Save. Any guidance?
Question 1
Get accustomed to null-rendering components, you almost can't avoid them with Recoil and, more in general, this hooks-first React world 😉
About the useRecoilValue inside a function: you're right, you should leverage useRecoilCallback for that kind of task. With useRecoilCallback you have a central point where you can get and set whatever you want at once. Take a look at this working CodeSandbox where I tried to replicate (the most minimal way) your use-case. The SaveData component (a dedicated component is not necessary, you could just expose the Recoil callback without creating an ad-hoc component) is the following
const SaveData = () => {
const saveData = useRecoilCallback(({ snapshot }) => async () => {
const ids = await snapshot.getPromise(carIds);
for (const carId of ids) {
const car = await snapshot.getPromise(cars(carId));
const carIndex = db.findIndex(({ id }) => id === carId);
db[carIndex] = car;
}
console.log("Data saved, new `db` is");
console.log(JSON.stringify(db, null, 2));
});
return <button onClick={saveData}>Save data</button>;
};
as you can see:
it retrieves all the ids through const ids = await snapshot.getPromise(carIds);
it uses the ids to retrieve all the cars from the atom family const car = await snapshot.getPromise(cars(carId));
All of that in a central point, without hooks and without subscribing the component to atoms updates.
Question 2
There are a few approaches for your use case:
creating empty atoms when the app starts, updating them, and saving them in the end. It's what my CodeSandbox does
doing the same but initializing the atoms through RecoilRoot' initialState prop
being updated by Recoil about every atom change. This is possible with useRecoilTransactionObserver but please, note that it's currently marked as unstable. A new way to do the same will be available soon (I guess) but at the moment it's the only solution
The latter is the "smarter" approach but it really depends on your use case, it's up to you to think if you really want to update the JSON at every atom' update 😉
I hope it helps, let me know if I missed something 😊