What are Props and State?
In short, props are inputs that we pass into components from their parent. State is the internal data that changes and re-renders the component.
Props
On every single React tutorial you will hear passing props, its almost like React is all about passing props. Word props is short for properties. In very simple terms, they are like attributes that we passed to HTML elements, like class. Props can contain different types of data, such as text, numbers, or functions, and can be passed down to nested components.
<div class="main-section"></div>
Imagine a bucket full of water and a garden. If the bucket represents
props, and the plants representcomponents, then watering the plants is likepassing propsordatato them. Without water, most plants will die, and without data, most apps won't function.
<Button variant="primary">
Accept
</Button>
<Button variant="secondary">
Decline
</Button>
We have two buttons here, with the variant prop being used to control their behavior and appearance. By passing primary and secondary values to the buttons, we can modify what they do and how they look, which is similar to passing CSS classes.
However, the variant prop can contain more than just styling - it can also include data and functions.
In summary, props are similar to CSS classes, but they are much more powerful and flexible. They allow you to pass data and functionality to components, making it easier to create highly customizable and reusable code.
State
Imagine visiting a beer company's website. In most countries, the company is required by law to ask for your age, as it is illegal to display alcoholic beverages to minors.
A pop-up window appears, asking you to enter your age or click on a button that says "Yes, I'm over 21 years old," at least in the 🇺🇸.
By clicking on that button, you will change the state of the component. If you click on "I'm over 21," app's state will change, and the website will be displayed. If not, you might be kicked out of the website."
Here is an example code if you would like to check it out. It's too long to post in here. Example code
Never mutate React State
React doesn't care what type of data you bind to state - it can handle anything from data to numbers and even functions. Although, if you're using functions, it's best to use useCallback to avoid any headaches.
It's important to be mindful of mutations. While you can mutate the state and it may work for your needs, it's not recommended. React relies on the fact that you're a cool developer who would never mutate the state. Therefore, it won't warn you about it if you do so. However, bugs caused by state mutation can be difficult to track down and fix.
When a component randomly renders or doesn't render, updates or doesn't update, it's like playing lottery. This can lead to confusion and frustration when trying to diagnose a problem. A quick fix by mutating the state can cause you endless hours and debugging time. That's why it's best to never mutate the React state, as it can save you loads of trouble in the long run.
Take a look at this code, and I will explain it line by line below. It isn't the full code because it would have too much boilerplate and my goal here is to explain how stuff works with minimal code.
const [currentColor, setCurrentColor] = React.useState(['#aaaaaa', '#bbbbb', '#ccccc'])
//// map function is used to get colors from currentColor
{currentColor.map((color, index) => {
const colorTable = \`color-\${index}\`;
///
////
<input
id={colorTable}
type="color"
value={currentColor}
onChange={(event) => {
const updatedColors = [...currentColor]
updatedColors[index] = event.target.value
setCurrentColor(updatedColors)
}}
/>
Let's break this code down:
You are probably familiar with this part. We have a useState("") and it's in array form. In the array, we have 3 colors, with their HEX values.
We have an input, and it is a browser's build in color picker. User will be able to pick colors. We know that from type="color".
value is set to currentColor, which is the default state of our useState hook.
const [currentColor, setCurrentColor] =
React.useState(['#aaaaaa', '#bbbbb', '#ccccc'])
///
<input
id={colorTable}
type="color"
value={currentColor}
/>
{currentColor.map((color, index) => {
const colorTable = \`color-\${index}\`;
onChange={(event) => {
const updatedColors = [...currentColor]
updatedColors[index] = event.target.value
setCurrentColor(updatedColors)
}}
}
Let's examine this part
We have an
onChangefunction, which get's invoked when there is anevent.When there is an event, we create a
new variable/arraycalledupdatedColors.updatedColorsvariable is equal to ouruseState()hook, which is in array form. Since it is an array, we get it's data with..., the spread operator.[...currentColor][index]comes from the.mapfunction.updatedColors[index]set's a color to theevent.target.value
- Finally, we
set the statewithsetCurrentColor
Okay, but why?
I can hear you asking, why are we mapping and creating a new array, and getting the values with the spread operator? What is the point of this?
If we directly mutate the currentColor array that comes from the useState hook, it will cause problems. React relies on the fact that you will never mutate the state directly.
So, we need to create an exact copy of the currentColor array and modify the copy. Which is updatedColors, and update our setCurrentColor with the updatedColors. With this implementation, React will receive a new object after each render and React will continue to control it's own state and we won't get involved in it's business. React like to perform it's magic under the hood, try not to touch it.
In short summary:
- Modifying the
currentColorarray is bad. - Creating a copy of the
currentColorarray and modifying the copy (updatedColors) is good.
- Make a new array
- Make your changes on the new array
- Update the state with that modified array
- Leave the original array alone, don't touch it.
Order is important.