React State (useState)
Links: 115 React Index
State¶
- Without React if we make a change to our state (like changing the value of some array) the UI won't be updated automatically.
- We will have to use JS to select the element (
document.getElementById
) and then making the changes.
- We will have to use JS to select the element (
- But as we know React is declarative, we just need to update our data and React will automatically react to it to update our UI.
Different between props and state¶
- Props refers to the properties being passed into a component in order for it to work correctly, similar to how a function receives parameters.
- Incoming props should be immutable and should never be changed manually i.e. the props should NEVER change from within the body of the component.
- You can think of it as fixed parameters that we are passing to a function which should never be changed inside the function body.
- We can obviously change it by reassigning it something else but we may get a warning.
- We can change the values that we pass to our component.
- Incoming props should be immutable and should never be changed manually i.e. the props should NEVER change from within the body of the component.
// this is WRONG
function Navbar(props) {
props.coverImage = "something else"
}
// this is OK
<Navbar image="hello" /> -> <Navbar image="hello2" />
- State refers to values that are managed by the component, similar to variables declared inside a function.
- Any time you have changing values that should be saved/displayed, you'll likely be using state.
useState
¶
- The first value that we give to
useState
is the default value of the state. - We use the callback function returned to us at the time of state definition to change the state.
- We cannot change the state directly like we do in normal JS since React needs to react to our state change and update the UI.
export default function App() {
const [isImportant, setIsImportant] = React.useState("Yes");
function handleClick() {
setIsImportant("No");
}
}
return (
<div onClick={handleClick}>
<h1>{isImportant}</h1>
</div>
);
}
If you ever need the old value of state to help you determine the new value of state, you should pass a callback function to your state setter function instead of using state directly.
- This callback function will receive the old value of state as its parameter, which you can then use to determine your new value of state.
- This is efficient and has to do with how react handles state changes and UI update internally.
const [count, setCount] = React.useState(0);
// this is correct
function add() {
setCount(prevCount => {
return prevCount + 1;
});
}
// this is not efficient
function subtract() {
setCount(count - 1)
}
Using arrays¶
- When setting an array we need to return a NEW array.
- Using
prevArray.push()
won't return a new array and it would just modify the existing array.- Moreover
.push()
operation returns an integer (number of items in the array).
- Moreover
const [thingsArray, setThingsArray] = React.useState(["Thing 1", "Thing 2"]);
// wrong
function addItem() {
setThingsArray((prevThingsArray) => {
return prevThingsArray.push("Something") // returns an integer
});
}
// right
function addItem() {
setThingsArray((prevThingsArray) => {
return [...prevThingsArray, "Something"]; // returning a new array
});
}
Using Objects¶
const [contact, setContact] = React.useState({
firstName: "John",
lastName: "Doe",
phone: "+1 (719) 555-1212",
email: "itsmyrealname@example.com",
isFavorite: false
})
function toggleFavorite() {
setContact(prevContact => {
return {
...prevContact,
isFavorite: true
}
})
}
- We are copying the whole
prevContact
and then overridingisFavorite
attribute.
Combining state and props¶
-
In this example a child component is receiving state via props.
// App component import React from "react"; import Count from "./Count"; export default function App() { const [count, setCount] = React.useState(0); function add() { setCount((prevCount) => prevCount + 1); } function subtract() { setCount((prevCount) => prevCount - 1); } console.log("App component rendered"); return ( <div className="counter"> <button className="counter--minus" onClick={subtract}> – </button> <Count number={count} /> <button className="counter--plus" onClick={add}> + </button> </div> ); } // Count component import React from "react"; export default function Count(props) { console.log("Count component rendered"); return ( <div className="counter--count"> <h1>{props.number}</h1> </div> ); }
-
When we first start our application we will see both the console log statements (App component rendered and Count Component rendered).
- Now if we click on the + or - button we will again see both the console log statements.
Whenever a state changes React will re-render the component where state exists (App
in the above example) and any child components that may rely on that state to be working correctly (Count
in the above example).
- If we want to provide the ability to child components to change state then we need to pass them reference to setter functions.
// App Component
function toggleFavorite() {
setContact((prevContact) => ({
...prevContact,
isFavorite: !prevContact.isFavorite,
}));
}
return <Star isFilled={contact.isFavorite} handleClick={toggleFavorite} />;
// Star Component
export default function Star(props) {
const starIcon = props.isFilled ? "star-filled.png" : "star-empty.png";
return (
<img
src={`../images/${starIcon}`}
onClick={props.handleClick}
/>
);
}
Last updated: 2023-03-26