No using class Component , only function in React - reactjs

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

Related

Modify only one field of the interface with the useState

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>

React state not updating props (using next.js)

Trying to update the state of a react-date-range object (next.js) in a component with useState / setState. Not sure what I'm doing wrong, but I keep getting an error that dateRange.startDate is undefined.
import {useState, React} from 'react'
import { DefinedRange } from 'react-date-range'
import PropTypes from 'prop-types'
export const DataCard = ({...props}) => {
let [dateRange, setDateRange] = useState([{
startDate: props.dateRange.startDate,
endDate: props.dateRange.endDate,
key: 'selection'
}])
const updateDateRange = (dateRange) => {
props.dateRange = {startDate: dateRange.startDate, endDate: dateRange.endDate}
setDateRange([props.dateRange.startDate, props.dateRange.endDate])
}
return (
<div>
<div>
<button>
<DefinedRange onChange={item=> {updateDateRange([item.selection])}} ranges={dateRange}/>
</button>
</div>
</div>
)}
DataCard.propTypes = {
header: PropTypes.string,
dateRangeComponent: PropTypes.element,
dateRange: PropTypes.object
}
DataCard.defaultProps = {
header: "Graph Name",
dateRange: {startDate: new Date(), endDate: new Date()}
}
Your dispatch code is a bit problematic.
const updateDateRange = (dateRange) => {
console.log(dataRange)
props.dateRange = {startDate: dateRange.startDate, endDate: dateRange.endDate}
setDateRange([props.dateRange.startDate, props.dateRange.endDate])
}
I suspect two things therefore i suggest to add a console.log.
Possibility 1
Maybe your input isn't an array, instead it's a e from event handler. So first need to confirm that by printing it onto screen.
Possibility 2
When you change something, you'd like to take changes from your input, not prop. So you need to think of why you are using props in the dispatch. Props are coming from parent, not children. Use local variable instead.
Bonus
Normally we don't render stuff inside a <button> element. It might be irrelevant to your problem, but might complicate your other parts of work. Use <div> instead.
Your initial status is an array of 1 object but when you setDateRange, you provide an array of two objects
Your init data:
[{
startDate: props.dateRange.startDate,
endDate: props.dateRange.endDate,
key: 'selection'
}] // an array of a object
setDateRange([props.dateRange.startDate, props.dateRange.endDate]) // setDateRange([a, b]), array of two objects
It should be:
setDateRange((prevState) => ({ startDate: props.startDate, endDate: props.endDate, key: prevState.key }));

Accessing array with Hooks and useState

I have the code below:
const [personsState, setPersonsState] = useState({
person: [
{ name: 'Max', age: 28 },
{ name: 'Manu', age: 29 },
{ name: 'Stephanie', age: 26 }
],
showPersons: false
});
Is it best practice to access the person array by using the code below?
persons = (
<div>
{personsState.person.map((person) => {
return <Person name={person.name} age={person.age} />;
})}
</div>
);
If you're using Hooks, React recommends that you separate out different types of state into different variables. Unlike class components, you shouldn't try to put all state into a single variable.
From their FAQ:
We recommend to split state into multiple state variables based on which values tend to change together.
Consider having persons (the array) and showPersons (the boolean) instead:
const [persons, setPersons] = useState([
{ name: 'Max', age: 28 },
{ name: 'Manu', age: 29 },
{ name: 'Stephanie', age: 26 }
]);
const [showPersons, setShowPersons] = useState(false);
And then generate JSX by mapping over the persons variable, instead of personsState.prson.
Your code looks good to me, only one extra suggestion which is related to Lists and Keys section of React documentation:
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity.
In order to eliminate warning, you need to add key to each iterated elements in .map() as:
<div>
{personsState.person.map((person, index) => {
return <Person key={index} name={person.name} age={person.age} />;
})}
</div>

React Table 7 - How to change the state of an individual button created dynamically by the React-Table API

I am using React Table 7 to create a table with the first cell of each row being a ChecOut/CheckIn Button. My goal is to create a tool where people can CheckOut and CheckIn equipment. My React Table CodeSandbox
The idea is that when a user clicks the button, it will change from "CheckOut" to "CheckIn" and vise versa. It will also change the color of the row when Checked out. I was able to accomplish this with JQuery (code is in the sandbox under the App() functional component) but want to avoid that and am sure it's easily doable in react.
My issue is changing the state of an individual button and which functional component to define it in. The <Button> elements are created dynamically by the React-Table, defined in the columns object under the App functional component.
{
Header: "CheckIn/Out",
Cell: ({ row }) => (
<Button
key={row.id}
id={row.id}
onClick={e => checkClick(e)}
value={checkButton.value}
>
{checkButton.value}
</Button>
)
}
I tried passing a function to onChnage attribute on the <Button> element. In that function I updated the state of the <Button> to change the string value from "CheckOut" to "CheckIn" but that does not work. At first, that changed the name (state) of All the <Button> elements. Not sure if the state will live in the App() component or the TableRe() functional component. Or do I need to use state at all?
I basically built this on top of the editable table code available on the React-Table website React-Table Editable Data CodeSandbox , examples section React-Table Examples .
Any help is much appreciated!
data.js
import React from "react";
// export default random => {
// let arr = [];
// for (let i = 0; i < random; i++) {
// arr.push({
// first: chance.name(),
// last: chance.last(),
// birthday: chance.birthday({ string: true }),
// zip: chance.zip()
// });
// }
// return arr;
// };
const temp_data = [
{
firstName: "johny",
lastName: "Bravo",
age: "23",
visits: "45",
progress: "complete",
status: "all done"
},
{
firstName: "johny",
lastName: "Bravo",
age: "23",
visits: "45",
progress: "complete",
status: "all done"
}
];
const ArrayData = () => {
const data = temp_data.map(item => {
return {
firstName: item.firstName,
lastName: item.lastName,
age: item.age,
visits: item.visits,
progress: item.progress,
status: item.status
};
});
return data;
};
// Do not think there is any need to memoize this data since the headers
// function TempData() {
// const data = React.useMemo(ArrayData, []);
// return data;
// }
export default ArrayData;

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