## React Concepts
🔹 *parent* [[⧋ React|React.js]]
▫️ *related* [[❞ React State|React State (R2R)]], [[❜ Controlled components in React|Controlled components in React]], [[❞ React Fundamentals (R2R)|React Fundamentals]]
### Overview
[TOC]
### Todo
- [ ] [State as a Snapshot](https://beta.reactjs.org/learn/state-as-a-snapshot)
- [ ] [Updating Objects in State](https://beta.reactjs.org/learn/updating-objects-in-state)
### Thinking in React
The React Docs have a really helpful chapter[^4] explaining how to think about a project before building it. I've realized that many of the biggest issues I've come upon so far have stemmed from starting a project without really thinking this part through.
A design can be split up into 3 parts:
- Programming - components should do one thing. Large components broken up.
- CSS - where to put class selectors
- Design - organizing layers
Each page can be broken down into the parent components and sub components.

- Main table with the List
- Search Bar
- Table of Products
- Product Categories
- Individual Products
Now that I understand the component Heirarcy, I can see right away where I would have put the component controlling state, and which components would be passed props. I read this before in Road to React but it makes a lot more sense now.
Since there isn't 2-way flow, this must be planned to make sure I know how components will relate to one another. It's also common to move components in the heirarchy as needs for components change.
In fact, it's probably helpful to first work without state components and only add when it becomes clear how each component will function.
How to decide if it's state?
- does it remain unchanged over time? Not state.
- Is it passed in from a parent? Not state.
- Can it be computed from other components which pass down props? Not state.
In the above case, to items (the search text and value of the checkbox) *both change over time but can't be computed from other components, so they're state*.
This means that the common parent must be where the state should live.
Once state has been set in the top-level, you then have to figure out how to pass the event handlers down to the nearest child components as attributes.
- "onChange" handlers will be that attributes while the actions to set state will be the matching variables.
- From there you can call the "onChange" handler in the component declaration as props, and finally assign it as a props attribute to the bottom level component.
**Filterable Product Table Component**
```jsx
//state
function FilterableProductTable({ products }) {
const [filterText, setFilterText] = useState('');
const [inStockOnly, setInStockOnly] = useState(false);
//Component definition
<SearchBar
filterText={filterText}
inStockOnly={inStockOnly}
onFilterTextChange={setFilterText}
onInStockOnlyChange={setInStockOnly} />
// Component instantiation through props
function SearchBar({
filterText,
inStockOnly,
onFilterTextChange,
onInStockOnlyChange
}) {
return (
<form>
<input
type="text"
value={filterText} placeholder="Search..."
// and event handler where change occurs
function SearchBar({
onChange={(e) => onFilterTextChange(e.target.value)} />
```
*This code defines a search bar component that allows the user to filter text and in stock only. It also includes two props, SearchBar with an input field which is displayed when clicked or changing it's value if any of them are changed.*
### Passing Props to a component
**Key takeaways:**
- Define the props in the component as an object
- return JSX object with props in brackets: `{name}`
- Then create an instance of the component with props defined as JSX attribute values: `<Profile name='My name' />`
```jsx
import { getImageUrl } from './utils.js';
// 1. define prop keys
function Profile({
imageId,
name,
profession,
awards,
discovery,
imageSize = 70
}) {
// 2. return JSX
return (
<section className="profile">
<h2>{name}</h2>
<img
className="avatar"
src={getImageUrl(imageId)}
alt={name}
width={imageSize}
height={imageSize}
/>
<ul>
<li><b>Profession:</b> {profession}</li>
<li>
<b>Awards: {awards.length} </b>
({awards.join(', ')})
</li>
<li>
<b>Discovered: </b>
{discovery}
</li>
</ul>
</section>
);
}
export default function Gallery() {
return (
<div>
<h1>Notable Scientists</h1>
{// 3. Define as attribute values}
<Profile
imageId="szV5sdG"
name="Maria Skłodowska-Curie"
profession="physicist and chemist"
discovery="polonium (chemical element)"
awards={[
'Nobel Prize in Physics',
'Nobel Prize in Chemistry',
'Davy Medal',
'Matteucci Medal'
]}
/>
<Profile
imageId='YfeOqp2'
name='Katsuko Saruhashi'
profession='geochemist'
discovery="a method for measuring carbon dioxide in seawater"
awards={[
'Miyake Prize for geochemistry',
'Tanaka Prize'
]}
/>
</div>
);
}
```
You can also pass information in a component using `{children}`:
- Define divs with `className` in component definition
- Put specific tags within `<Component>`
- [I] In this example `<Card>` is just a shorthand for two wrapping `<div>` tags.
```jsx
function Card({ children, title }) {
return (
<div className="card">
<div className="card-content">
<h1>{title}</h1>
{children}
</div>
</div>
);
}
export default function Profile() {
return (
<div>
<Card title="Photo">
<img
className="avatar"
src="https://i.imgur.com/6WvNhk4.jpg"
alt="Rainbow Bright"
width={100}
height={100}
/>
</Card>
<Card title="About">
<p>Rainbow Bright has a friend named Twink and a magnificent horse named Starlite.</p>
</Card>
</div>
);
}
```

### React Component Composition
Complete notes for this.[^2]
### useRef() focus
**Input with Label Component**
```jsx
const InputWithLabel = ({
id,
value,
type = 'text',
onInputChange,
isFocused,
children,
}) => {
// A
const inputRef = React.useRef();
// C
React.useEffect(() => {
if (isFocused && inputRef.current) {
// D
inputRef.current.focus();
}
}, [isFocused]);
return (
<>
<label htmlFor={id}>{children}</label>
{/* B */}
<input
ref={inputRef}
id={id}
type={type}
value={value}
onChange={onInputChange}
/>
</>
);
};
```
*This code creates a label that allows the user to focus or select an input element when clicked. It also sets up effects for focused inputs, and displays it in one of its children if they are focused before displaying them*
### Children
> *You can think of a component with a children prop as having a “hole” that can be “filled in” by its parent components with arbitrary JSX. You will often use the children prop for visual wrappers: panels, grids, and so on.*

### Lifting up state
A slider I was building for my portfolio wouldn't work the way I expected, it turned out to be a great example of "lifting up state" and component heirarchy, and I need to think it through to re-organize and fix everything.
The big thing is that I was trying to trigger changes in another child element using state from navigation on the same level, which of course didn't make sense.
The only way to give the nav items the ability to change the stateful list that changing the card components was to remove their state, have it instead sent in the parent element, and communicate with them through props in the component instance.
Even once I figured this out, it was really difficult to figure out how and where to move the props to make it most effective.
I then ran into an issue in which the wrong items were being filtered from the list and I had to reorganize the call back handlers to make them load correctly.
### Updating arrays without mutation
Overview:
- You can put arrays into state, but you can’t change them.
- Instead of mutating an array, create a new version of it, and update the state to it.
- You can use the `[...arr, newItem]` array spread syntax to create arrays with new items.
- You can use filter() and map() to create new arrays with filtered or transformed items.
- You can use Immer to keep your code concise.
---
Updating an array in React requires that you pass an entirely new array rather than updating and mutating the current array, same as other objects.
The best ways to avoid mutation are either through using not mutating methods like `filter()`, `map()`, or by using methods that copy the existing array to a new array, like the spread operator.
Here's a table for checking alternative array methods to common JS operations. You can also use [[Immer]].

>[!warning]
> Be careful especially of mistaking `splice()` and `slice()`, which sound the same but are very different. `splice()` will mutate the original array while `slice()` only copies a partial or entire array.
>
> A good way to think of it might be that *splicing* film requires cutting and editing a film reel permanently (very dangerous), but having a *slice* is just sharing a bit of pizza with another person. It's a bit of a stretch but makes some sense.
### Spread instead of Push
In order to avoid using `push()` the best way is to use the spread operator, which creates a new array with existing items followed by new items.
```jsx
const [artists, setArtists] = useState([]);
setArtists( // Replace the state
[ // with a new array
...artists, // that contains all the old items
{ id: nextId++, name: name } // and one new item at the end
]
);
// Or in a button
<button onClick={() => {
setName('');
setArtists([
...artists,
{ id: nextId++, name: name }
]);
}}>Add</button>
```
You can also *prepend* and item simply by switching the order of the spread value and the added values, this makes it possible to replace `push()` and `unshift()` operations.
---
### Filter instead of remove
Rather than removing an item from an array, the alternative way is to use `filter` to filter matching items out before returning a new array without the match.
“create an array that consists of those artists whose IDs are different from artist.id”
**Delete Artist Button**
```jsx
<button onClick={() => {
setArtists(
artists.filter(a =>
a.id !== artist.id
)
);
}}>
Delete
</button>
```
This code sets the artist ID of a button when clicked, and removes all active artists from an array.
---
### Map instead of replacing
Instead of replacing an item in the state (i.e., `arr[0] = 'bird'`), in react you want to create a variable that maps over the current array and the replaces the entire array with the original array + any changes.
**Counter List with Increment Click Handler**
```js
import { useState } from 'react';
let initialCounters = [
0, 0, 0
];
export default function CounterList() {
const [counters, setCounters] = useState(
initialCounters
);
function handleIncrementClick(index) {
const nextCounters = counters.map((c, i) => {
if (i === index) {
// Increment the clicked counter
return c + 1;
} else {
// The rest haven't changed
return c;
}
});
setCounters(nextCounters);
}
```
*This code defines a React component called CounterList that handles the increment click on an array of counters. It also includes functions to update and set their counter values based on its index, which can be used in react context.*
---
### Inserting into an array using Slice
Inserting into an array requires that you define items before and after an insertion point, as well as the new items that you want to add.
**React List with Insertion and Click Handler**
```js
import { useState } from 'react';
let nextId = 3;
const initialArtists = [
{ id: 0, name: 'Marta Colvin Andrade' },
{ id: 1, name: 'Lamidi Olonade Fakeye'},
{ id: 2, name: 'Louise Nevelson'},
];
export default function List() {
const [name, setName] = useState('');
const [artists, setArtists] = useState(
initialArtists
);
function handleClick() {
const insertAt = 1;
const nextArtists = [
// Items before the insertion point:
...artists.slice(0, insertAt),
// New item:
{ id: nextId++, name: name },
// Items after the insertion point:
...artists.slice(insertAt)
];
setArtists(nextArtists);
setName('');
}
```
*This code defines a React component called "List" that creates an array of artists and sets the next artist to 'Lamidi Olonade Fakeye' when clicked. It also includes callbacks for adding new items, removing old item*
### Reverse and Sort
Similarly, making changes that change the existing items in an array requires that you first make a copy of the original array before changing it and then replacing the original array with the changed array.
```js
import { useState } from 'react';
let nextId = 3;
const initialList = [
{ id: 0, title: 'Big Bellies' },
{ id: 1, title: 'Lunar Landscape' },
{ id: 2, title: 'Terracotta Army' },
];
export default function List() {
const [list, setList] = useState(initialList);
function handleClick() {
const nextList = [...list];
nextList.reverse();
setList(nextList);
}
```
### Using Immer
[[Immer]] can also be used to make changes to a *draft* of a list in order to make succinct updates.
**Art Bucket List Component**
```jsx
import { useState } from 'react';
import { useImmer } from 'use-immer';
let nextId = 3;
const initialList = [
{ id: 0, title: 'Big Bellies', seen: false },
{ id: 1, title: 'Lunar Landscape', seen: false },
{ id: 2, title: 'Terracotta Army', seen: true },
];
export default function BucketList() {
const [myList, updateMyList] = useImmer(
initialList
);
function handleToggleMyList(id, nextSeen) {
updateMyList(draft => {
const artwork = draft.find(a =>
a.id === id
);
artwork.seen = nextSeen;
});
}
return (
<>
<h1>Art Bucket List</h1>
<h2>My list of art to see:</h2>
<ItemList
artworks={myList}
onToggle={handleToggleMyList} />
<h2>Your list of art to see:</h2>
<ItemList
artworks={yourArtworks}
onToggle={handleToggleYourList} />
</>
);
}
function ItemList({ artworks, onToggle }) {
return (
<ul>
{artworks.map(artwork => (
<li key={artwork.id}>
<label>
<input
type="checkbox"
checked={artwork.seen}
onChange={e => {
onToggle(
artwork.id,
e.target.checked
);
}}
/>
{artwork.title}
</label>
</li>
))}
</ul>
);
}
```
*This code creates a list of artworks and displays them in an HTML page with the title "Big Bellies Landscape Army" when clicked.*
A mutation like this is alright with Immer.
```js
updateMyTodos(draft => {
const artwork = draft.find(a => a.id === artworkId);
artwork.seen = nextSeen;
});
```
### Comparison of functions - traditional and using Immer
```js
export default function TaskApp() {
const [todos, setTodos] = useState( // useImmer(
initialTodos
);
/* Add function */
function handleAddTodo(title) {
setTodos([
...todos,
{
id: nextId++,
title: title,
done: false
}
]
)
}
// OR using immer
function handleAddTodo(title) {
setTodos(draft => {
draft.push({
id: nextId++,
title: title,
done: false
});
});
}
/* Edit function */
function handleChangeTodo(nextTodo) {
setTodos(todos.map(item => {
if (item.id === nextTodo.id) {
return nextTodo
} else {
return item
}
}));
}
// OR using Immer
function handleChangeTodo(nextTodo) {
setTodos(draft => {
const todo = draft.find(t =>
t.id === nextTodo.id
);
todo.title = nextTodo.title;
todo.done = nextTodo.done;
})
}
/* Delete function */
function handleDeleteTodo(todoId) {
setTodos(
todos.filter(item => item.id !== todoId)
);
}
// OR using Immer
function handleDeleteTodo(todoId) {
setTodos(draft => {
const todoIndex = draft.findIndex(t =>
t.id === todoId
);
draft.splice(todoIndex, 1);
});
}
```
### Managing State
See [[❞ Managing State - state structure|Managing State]]
<hr class="references">
### References
[^1]: React.dev documentation (2022). *Learn React Tutorial*, [React.dev](https://react.dev/learn)
[^2]: React Component Composition, Robin Wieruch (2019). [React Component Composition](https://www.robinwieruch.de/react-component-composition/)
[^3]: Composition vs Inheritance. [React Docs](https://reactjs.org/docs/composition-vs-inheritance.html)
[^4]: Thinking in React, 2022. [React Docs](https://beta.reactjs.org/learn/thinking-in-react)
[^5]: React free library. [Github](https://github.com/EbookFoundation/free-programming-books/blob/main/books/free-programming-books-langs.md#react)