Modify only one field of the interface with the useState - reactjs

I defined this interface and hook with an initialization:
interface userInterface {
email:string
name:string
last_name:string
}
const [userData, setUserData] = useState <userInterface> ({
email:"",
name:"",
last_name:"",
})
then if you just wanted to change the name only. How should it be done with setUserData?
That is, I want to leave the email and the last_name as they are but only modify the name

#Jay Lu is correct.
I wanted to note a couple things.
Typescript Interfaces
Interface name should use PascalCasing
Key names should use camelCasing
Prefer utility types where appropriate
[optional] Interface name should be prefaced with a capital "I"
This used to be a convention now more people are moving away from it. Your choice
Interface Update: 1) Naming Conventions
interface UserInterface {
email:string
name:string
lastName:string
}
The next thing we want to do is use a utility type to simplify the verbosity of our code.
Interface Update: 2) Utility Type
We will actually change this interface to a type to easily use the utility type Record.
type UserInterface = Record<"email" | "name" | "lastName", string>;
As for your component, you didn't provide much detail there so I will provide details on setting state.
Functional Component: Updating State
It's very important to establish what variables or data you need to "subscribe" to changes. For instance, if the email and name are static (never changing) there is no need to have them in state which would result in a state variable defaulting to an empty string:
const [userData, setUserData] = useState("");
If that's not the case and you indeed need to update and manage the email, name, and lastName updating state is simple: spread the existing state with the updated value. You do this using the setter function provided in the tuple returned from useState. In this case that's setUserData. The setter function takes a value that is the same type as your default or it can accept a callback function where it provides you the current state value. This is really powerful and really useful for updating a state object variable. In your case we have:
setUserData((previous) => ({...previous, name: "Updated name value"});
What's happening here? The setUserData provides us the "previous" state if we pass it a callback. On the first call of this function "previous" is:
{
email: "",
name: "",
lastName: ""
}
We are taking that value and spreading it over in a new object with the updated value. This is the same as Object.assign. If you spread a key that already exists in the object it will be replaced. After we spread our state object looks like:
{
email: "", // didn't change because we didn't pass a new value
lastName: "", // didn't change because we didn't pass a new value
name: "Updated name value" // changed because we passed a new value
}
Which means, if you wanted to update the email you can by simply doing:
setUserData((previous) => ({...previous, email: "hello#world.com"});
Now your state object will be:
{
email: "hello#world.com",
lastName: "",
name: "Updated name value"
}
And if you call setUserData with a callback again, the previous value with be that object above.
If you want to set it back to the original values you can update the entire state without using the callback. Why? Because we don't need to preserve any values since we want to overwrite it:
setUserData({ email: "", lastName: "", name: ""});
There is a slight improvement to that though. If we decide that at some point we want to "reset to default" we should store the default value in a variable and reuse it. Not 100% necessary but it might be a good update especially if you have a complex component.
Quick Note on the Power of Typescript
If you were to try and update state with a new key that you didn't have defined before let's say "helloWorld" typescript will give you an error because "helloWorld" is not defined in your UserData type.
Hopefully #Jay Lu's answer and some of this info helped. If you provide more details we might be able to offer more guidance.

Simple expample:
setUserData((prev) => ({ ...prev, name: 'Name you want' }));
const { useState } = React;
const DemoComponent = () => {
const [userData, setUserData] = useState({
email: "",
name: "",
last_name: ""
});
const handleBtnOnClick = (name) => {
setUserData((prev) => ({ ...prev, name: name }));
};
return (
<div>
<button
onClick={() => {
handleBtnOnClick("Jay");
}}
>
Jay
</button>
<button
onClick={() => {
handleBtnOnClick("Andy");
}}
>
Andy
</button>
<button
onClick={() => {
handleBtnOnClick("Olivia");
}}
>
Olivia
</button>
<div>{JSON.stringify(userData, null, "\t")}</div>
</div>
);
}
ReactDOM.render(
<DemoComponent />,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Related

setState not updating state with multiple varaibles

I am working on one reactjs project. where the user needs to pick two different dates starting date and end date. When a user picks first date i need to update the state to check whether the starting date should be less than closing date ,But the problem i am facing is setState not updating state immediately. I am facing this problem during update the store timings .How can i fix this problem.
const [storeTimings, setStoreTimings] = useState({
startTime: "",
endTime: "",
startTime2: "",
endTime2: "",
});
const isValidTime = (key, value) => {
setStoreTimings({ ...storeTimings, [key]: value });
console.log({ key, value });
};
<DatePicker
onChange={(date) => {
isValidTime("startTime", date);
}}
/>
State setters can also take a function to update the state. This callback is given the previous state which is what you want.
You should use an updater function instead because using storeTimings directly is not guaranteed to reference the state of your component when you want to update it. Using this function to get the previous state directly before the update allows you to always use the latest state:
const [storeTimings, setStoreTimings] = useState({
startTime: "",
endTime: "",
startTime2: "",
endTime2: "",
});
const isValidTime = (key, value) => {
// provide updater function
setStoreTimings((previous) => ({ ...previous, [key]: value }));
console.log({ key, value });
};
<DatePicker
onChange={(date) => {
isValidTime("startTime", date);
}}
/>
More details about this function argument to state setters can be found in this question.

React - Set state object using args

I have an object saved in a state I am trying to create a function to update parts of the object.
function removeError(val) {
setPersonsState((prevState) => ({
...prevState,
val: "",
}));
}
I have tried to pass val to the object key to update it.
onClick={removeError('Bob')}
Where Bob replaces val to update in the object. Sorry if terminology is bad.
Well, you should change it to be in the following way, sine you don't want to change a key named val, but the key with name equal to the value of val
function removeError(val) {
setPersonsState((prevState) => ({
...prevState,
[val]: "",
}));
}
And, as suggested in the comments by #RoshanKanwar, also the click handler is not correct, it should be
onClick = {() => removeError('Bob')}

No using class Component , only function in React

Hello I am just learning react and I am looking at tutorials but in the version that was installed, react is no longer using the classes, it only appears functions and I would like to continue that way if possible, but I have a problem with where to change the name in this part with a click but it does not allow me to access persons(const), how could I do it?
import Person from './Person/Person'
function App() {
const persons = {
persons: [
{ name: 'Jose', age: 32},
{ name: 'Gabriel', age: 2}
]
}
const switchNameHandler = () => {
persons({
persons: [
{ name: 'Jose Fernando', age: 32},
{ name: 'Gabriel', age: 2}
]
})
}
return (
<div className="App">
<h1>Hi, I'm a React App!</h1>
<button onClick={switchNameHandler}> Switch Name</button>
<Person name={persons.persons[0].name} age={persons.persons[0].age}/>
<Person name={persons.persons[1].name} age={persons.persons[1].age}> I like play</Person>
</div>
);
}
export default App;
How could I fix the switchNameHandler part?
I know that if I use classes I can access this.setPersons, but is there any way to access without using classes?
You need to use the useState hook. All hooks return two things in a de-structured array (1) the state value and (2) the function to set that state value. and the value you put in the useState() function is the initial value.
For example:
const [name, setName] = useState("Ben");
Here the initial value of name is "Ben". If I wanted to change that value, I could use the setName function and do something like this setName("Jerry");. Now the value of name is "Jerry".
The biggest difference between the setter (in this case setName) and this.setState (in a class component), is that this.setState remembers and spreads the previous state automatically for you if you don't explicitly define it. With hooks, you have to do that your self:
For example:
const [person, setPerson] = useState({ name: "", id: "", age: "" });
If I have this state, and want to edit just the name of the person, I have to remember to spread the previous state object every time I update state, by using a callback function - where the parameter in the callback is the previous state:
// just updating name
setPerson(prevPerson => ({ ...prevPerson, name: "Bob" }));
Here, the only thing that changes was the "name" value and everything else stayed the same:
Result: { name: "Bob", id: "", age: ""}
Check out the react documentation for more tips and examples: https://reactjs.org/docs/hooks-state.html

react select is not picking up my selected value using typescript

I am sorry if my question is silly, I am fairly new to Reactjs,
Please do not tell me to useHooks as I can't use it in my current project.
I am working on a form and on it's validations.
my relevant code:
//part of my state
this.state = {
form: {
country: null,
zipCode: "",
},
formErrors: {
country: null,
},
countryList: Array<string>(),
};
}
<Select name="country" defaultValue={countryOptions[1]} options={countryOptions} value={form.country} onChange={(e) => {
this.handleCountryChange({value : e.value, label: e.value })
}}
/>
{this.state.formErrors.country && (
<span className="err">{this.state.formErrors.country}</span>
)}
handleCountryChange(e : countrySelection){
const selectedCountry = e.value;
console.log(selectedCountry);
var formAux = { ...this.state };
formAux.form.country = selectedCountry;
}
but sadly my select keeps looking like this:
Even after I pick a country and I can see it with the console log above. What is missing here in order for the select to pick up the value?
You have to do this something like this
How to set a default value in react-select
Here they did not use defaultValue property
As well as check other properties which they used.
As well as refer this answer of this question too
react select not recognizing default value
You are not setting the state correctly, you can not mutate the state like that. You need to use setState API. See docs: https://reactjs.org/docs/react-component.html#setstate
In your case the event handler should be like this:
handleCountryChange(e : countrySelection){
const selectedCountry = e.value;
// you said this works correctly
console.log(selectedCountry);
this.setState(currentState => ({
// spread the entire state so that other keys in state remain as they are
...currentState,
form: {
// spread state.form so that other keys in state.form remain as they are
...currentState.form,
// only update state.form.country
country: selectedCountry
}
}));
}
Please read the code comments, that should help you understand.

How to use useState with {}?

given this..
const [question, setQuestion] = useState({})
and question can contain a title and a description.
I.E.
{
title: 'How to use useState with {}?',
decription: 'given this..`const [question, setQuestion] = useState({})`and `question` can contain a title and a description. I.E. { title: 'How to use useState with {}?', decription: '' }How can the title and description be set independently with `setQuestion`?'
}
How can the title and description be set independently with setQuestion?
The setter function you get from useState() expects to be passed argument which will entirely replace the old state. So you can't use it to update just the title, without also passing in all other properties.
But you can derive a new state object from the existing one and then pass that whole object to setQuestion()
setQuestion({
...question,
title: "New title",
})
One thing that I like to do in this situation is to use the React.useReducer hook instead:
function App() {
const initialState = {
title: 'My title',
description: 'My description'
}
const [state, setState] = React.useReducer(
(p, n) => ({ ...p, ...n }),
initialState
)
return (
<div>
<p>{state.title}</p>
<p>{state.description}</p>
<button onClick={() => setState({ description: 'New description' })}>
Set new description
</button>
</div>
)
}
This way, you're only changing the properties that you want and don't have to deal with copying the old state object and creating a new one based on old and new values.
Also, it will probably look more familiar to you if you're just starting with hooks because of the similarity to the this.setState() calls inside class components.
Here's a little example that shows this approach in action:
example
If this is a common pattern you use then I'd suggest writing your own hook to implement the functionality you want.
If it's a one-off thing then you can use the object spread operator to do it fairly cleanly:
setQuestion({
...question,
title: 'updated title',
});
Here is what it would look like pulled out into a separate hook:
const useMergeState = (initial) => {
const [state, setState] = React.useState(initial);
const mergeState = React.useCallback((update) => setState({...state, ...update}));
return [state, mergeState];
};
The official Docs says:
Both putting all state in a single useState call, and having a
useState call per each field can work. However, components tend to be most
readable when you find a balance between these two extremes, and group
related state into a few independent state variables.
Eg:
const [position, setPosition] = useState({ left: 0, top: 0 });
const [size, setSize] = useState({ width: 100, height: 100 });
In case the state logic becomes complex, we recommend managing it with a reducer or a custom Hook.
Also, remember in case you are using any state object in the useState({}) hook, that unlike the classic this.setState, updating a state variable always replaces it instead of merging it, so you need to spread the previous state in order not to lose some of his properties eg: setResource(…resource, name: ‘newName’);
setQuestion({
title: 'YOUR_TITLE',
description: 'YOUR_DESCRIPTION',
id: '13516',
})
State cannot be edited. You need to set new value always. Thus, it can be replaced with spread operator as below.
setQuestion({
...question,
title: 'new title'
})
or
const newQuestion = { ...question, title: 'new title }
setQuestion(newQuestion)

Resources