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.
Related
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})
I'm having a little bit of difficulty figuring out how to reuse components in many places.
Obviously the reason why react is so good is because you can create common components and reuse them across many different pages, or just multiple times in the same page.
I have this component for displaying a dropdown that a user can use to select a new value, and change their settings. This component is reused in many different places on my website.
<ChannelSelect
channelID={
saveEnabled.saveEnabled.newSettings.verificationPanel.channelID
}
settings={
saveEnabled.saveEnabled.newSettings.verificationPanel.channelID
}
/>
What I am struggling with is telling this component which key: value in the state (object) to change. This is because simply passing in the code below, doesn't actually allow me to change the state of this specific key.
settings={saveEnabled.saveEnabled.newSettings.verificationPanel.channelID}
In my select menus onChange event I have the code snippet below. However, this doesn't actually allow me to change the specific value declare, it would actually just overwrite the whole object.
const { saveEnabled, setSaveEnabled } = useContext(SaveContext);
const onChange = (event) => {
props.settings = "newValue"
setSaveEnabled(props.settings)
Any idea how I can do this?
I am using this libray https://material-table.com/#/ to make my table with react js.
In my table I use selection :
And actions as well :
What happens :
I have the same action(DELETE) when selecting a row and my positioning actions (SAVE, DELETE).
When clicking the selection action I want to show a dialog which title will be users if I select more than 1 row. When clicking my right action I want to show the same dialog but with the title user.
I am thinking in using ternary operator to make the condition like the following:
<DialogTitle>
rowsCount > 1 ? (
'Users'
) : (
'User'
)
</DialogTitle>
Question:
How can I know how many rows are selected and pass that data to my dialog in the const RowsCount?.
UPDATE :
Following the first answer given , to figure out if the table it's selected or not.. I've found a prop that my parent component(ModuleLayout) has : tableCanSelect which is boolean. The thing is that I don't know how to pass it to the child , which is my current table.
I suppose selected is a const .
I tried to do it like the following :
const MYTABLE : FC = () => {..
.. some other constants ..
const selected = useState<typeof ModuleLayout | null(ModuleLayout.tableCanSelect);
}
But it throws me this error :
Property 'tableCanSelect' does not exist on type 'FC<Props>'.
Which is not true because the prop tableCanSelect is indeed a prop of my parent component ModuleLayout.
Note : My dialog is a different component than my table which means I import my dialog to show it in my table.
I don't know anything about the table you're using, but I quickly looked up the documentation. You can use actions on your rows.
actions={[
{
icon: 'save',
tooltip: 'Save User',
onClick: (event, rowData) => alert("You saved " + rowData.name)
}
]}
The onClick part is what you need to count the amount of users. So you'd have something like this:
...
const Component = () => {
const [total, setTotal] = useState(0)
...
// table things here
data={[
// data here
]}
actions={[
{
icon: 'selection',
tooltip: 'Select',
onClick: (event, rowData) => selected ? setTotal(total + 1) : setTotal(total - 1),
]}
...
}
The code above is not completely correct, you'll have to figure out how to see if it's selected or unselected. But using that you can set the state and increase or decrease the total. Then you can use that total in your dialogue by passing it as a prop to change the title.
EDIT: Update to your update.
You need:
options={{
selection: true
}}
On your table according to https://material-table.com/#/docs/features/selection.
You need to store the state for your rows in the component that's importing the table. You can do this using:
const example = useState(true)
// or const example = useState(rowsSelected)
// depending on what your table needs
Doing:
const selected = useState<typeof ModuleLayout | null(ModuleLayout.tableCanSelect);
Doesn't really make sense, the reason why you're getting your error is likely because it needs to be useState<Props>. It will get the type of your component and try store that.
You also shouldn't store this state in the table itself, you should store it where you want to use it and pass it to the table as a prop. In this case you want to store it in the parent so that you can also use it in the dialogue, so you'll have to pass down the props to both components.
That being said I don't see any way to count all the selected rows in the documentation, nor do I see any way to count/select individual rows, so I'd really suggest that you either find a different table to use (chakra/semantic ui/something else) or better yet try and do it yourself without using 3rd party library stuff. Using 3rd party library stuff is nice until the point where you want to add custom things then it becomes really complicated.
As a tip, I can also see that you're trying to do a lot of things at once and getting confused between a lot of it, the table, the types, the parent/child component relationship and state updates, it's too much to try and learn and do all at once. Instead choose one thing at a time and make sure you understand it. Focus on learning just state, how props work, then focus on creating a child component and trying to use parent state in there, then learn about types and after that try and create a table in the way you want to. At that point rather create your own table without using a 3rd library so that you can customize it in the way you want and also so you'll learn a lot more.
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 😊
I'm working on a modal component where users can edit the contents of a post they created.
I have it set up where a notification window pops up if the user tries to close the modal without saving.
However, sometimes users don't make any changes and I don't want them to see this notification, I just want the modal to close.
I could create separate states to store the original state then compare those states to the current states, but I have a lot of states (15) and that seems messy.
I'm wondering if there is a way for me to check if any set of particular states have been changed at all? That way, I would know to show the notification or not.
It seems that you use hooks, so in your case I would suggest to refactor a little bit your component with the states like below and after this has been done to use the JSON.stringify to check if something has been changed. You will actually have one state as an object.
const [var1, setVar1] = useState($initialVar1);
const [var2, setVar2] = useState($initialVar2);
const [var3, setVar3] = useState($initialVar3);
...
const [var14, setVar14] = useState($initialVar14);
to
const [componentState, setComponentState] = useState({
var1: $initialVar1,
var2: $initialVar2,
var3: $initialVar3
...
})
Wherever you want to update the individual states you can use the spread operator
setComponentState({
...componentState,
varX: newValue
})