Rename articles prefixed with react-
This commit is contained in:
37
snippets/react/s/conditional-classname.md
Normal file
37
snippets/react/s/conditional-classname.md
Normal file
@ -0,0 +1,37 @@
|
||||
---
|
||||
title: "Tip: React conditional className, empty strings and null"
|
||||
shortTitle: Conditional className
|
||||
type: tip
|
||||
language: react
|
||||
tags: [components]
|
||||
cover: succulent-red-light
|
||||
excerpt: In React components, you might need to conditionally apply a `className`. Learn how to handle empty values correctly using this handy tip.
|
||||
dateModified: 2021-11-07T16:34:37+03:00
|
||||
---
|
||||
|
||||
When developing React components, you often need to conditionally apply a `className` attribute to one or more elements. Sometimes, you will have two or more possible values depending on a condition. But there are also times that you might apply a `className` based on a condition or leave it completely empty otherwise.
|
||||
|
||||
There is a correct way to handle a conditional empty `className` and an incorrect one. Surprisingly, the incorrect way is pretty common and examples of it can be found all around the web. Consider the following code:
|
||||
|
||||
```jsx
|
||||
const MyComponent = ({ enabled }) => {
|
||||
return ( <div className={enabled ? 'enabled' : ''}> Hi </div> );
|
||||
};
|
||||
|
||||
const OtherComponent = ({ enabled }) => {
|
||||
return ( <div className={enabled ? 'enabled' : null}> Hi </div> );
|
||||
};
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<>
|
||||
<MyComponent enabled={false} />
|
||||
<OtherComponent enabled={false} />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
In this code example, we define two very similar components. Both of them conditionally set the `className` of an element based on the value of the `enabled` prop. The first one will set the `className` to an empty string if `enabled` is `false` and the second one will set it to `null`.
|
||||
|
||||
The resulting output is pretty similar. However, if you carefully inspect the HTML, you will notice that the first one will render `<div class>Hi</div>` whereas the second one will render `<div>Hi</div>`. This kind of markup (an attribute being present but without value) is rather uncommon and you'd rarely ever see something like that without React. This subtle difference is quite important and might be the root of a lot of problems, especially when writing CSS selectors for elements with/without any classes (e.g. `[class]`/`:not([class])`).
|
||||
|
||||
Therefore, you should prefer `null` when you don't want to add a `className` to an element, instead of an empty string. It keeps the markup cleaner and might help prevent some potential issues.
|
||||
53
snippets/react/s/proptypes-objectof-vs-shape.md
Normal file
53
snippets/react/s/proptypes-objectof-vs-shape.md
Normal file
@ -0,0 +1,53 @@
|
||||
---
|
||||
title: React PropTypes - objectOf vs shape
|
||||
shortTitle: PropTypes - objectOf vs shape
|
||||
type: story
|
||||
language: react
|
||||
tags: [components,proptypes]
|
||||
author: chalarangelo
|
||||
cover: shapes
|
||||
excerpt: Learn the differences between `PropTypes.objectOf()` and `PropTypes.shape()` and where to use each one with this quick guide.
|
||||
dateModified: 2021-06-12T19:30:41+03:00
|
||||
---
|
||||
|
||||
The `prop-types` package is used by millions of React developers every day in order to type check the props passed to their components. Most of us are probably familiar with a variety of its built-in validators, but many a developer seems to fall short when dealing with object props. Luckily, the `PropTypes.objectOf()` and `PropTypes.shape()` validators are here to help.
|
||||
|
||||
### PropTypes.shape()
|
||||
|
||||
The `PropTypes.shape()` validator can be used when describing an object whose keys are known ahead of time, and may represent different types. For example:
|
||||
|
||||
```js
|
||||
import PropTypes from 'prop-types';
|
||||
// Expected prop object - keys known ahead of time
|
||||
const myProp = {
|
||||
name: 'John',
|
||||
surname: 'Smith',
|
||||
age: 27
|
||||
};
|
||||
// PropTypes validation for the prop object
|
||||
MyComponent.propTypes = {
|
||||
myProp: PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
surname: PropTypes.string,
|
||||
age: PropTypes.number
|
||||
})
|
||||
};
|
||||
```
|
||||
|
||||
### PropTypes.objectOf()
|
||||
|
||||
The `PropTypes.objectOf()` validator is used when describing an object whose keys might not be known ahead of time, and often represent the same type. For example:
|
||||
|
||||
```js
|
||||
import PropTypes from 'prop-types';
|
||||
// Expected prop object - dynamic keys (i.e. user ids)
|
||||
const myProp = {
|
||||
25891102: 'johnsmith',
|
||||
34712915: 'ducklord',
|
||||
76912999: 'mrquacks'
|
||||
};
|
||||
// PropTypes validation for the prop object
|
||||
MyComponent.propTypes = {
|
||||
myProp: PropTypes.objectOf(PropTypes.number)
|
||||
};
|
||||
```
|
||||
187
snippets/react/s/redux-readable-reducers.md
Normal file
187
snippets/react/s/redux-readable-reducers.md
Normal file
@ -0,0 +1,187 @@
|
||||
---
|
||||
title: Writing readable reducers in Redux
|
||||
shortTitle: Readable reducer tips
|
||||
type: story
|
||||
language: react
|
||||
tags: [logic]
|
||||
author: chalarangelo
|
||||
cover: beach-from-above
|
||||
excerpt: When working with stateful code, you can run into issues realted to complexity and readability. Oftentimes, they are easily fixable.
|
||||
dateModified: 2021-06-12T19:30:41+03:00
|
||||
---
|
||||
|
||||
_This article's examples are based on Redux, where the issues described are more common. As these issues are not limited to Redux, you might still find some value in the tips and solutions presented if you are struggling with maintaining complexity and readability in your code._
|
||||
|
||||
When working with state in your code, you might often run into issues with maintaining complexity, keeping the code readable and even figuring out how to properly test it. Oftentimes, these issues are easily fixable if you take a step back and identify the root of the problem.
|
||||
|
||||
Let's start with an example of what a redux reducer might look like. We'll follow this example throughout this post, making changes and improvements, so make sure you understand it before continuing.
|
||||
|
||||
```js
|
||||
const initialState = {
|
||||
id: null,
|
||||
name: '',
|
||||
properties: {},
|
||||
};
|
||||
|
||||
const generateID = () => Math.floor(Math.random() * 1000);
|
||||
|
||||
const reducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case 'createID':
|
||||
return {
|
||||
...state,
|
||||
id: generateID(),
|
||||
};
|
||||
case 'setName':
|
||||
return {
|
||||
...state,
|
||||
name: action.name,
|
||||
};
|
||||
case 'addProperty':
|
||||
return {
|
||||
...state,
|
||||
properties: {
|
||||
...state.properties,
|
||||
[action.propertyName]: action.propertyValue,
|
||||
},
|
||||
};
|
||||
case 'removeProperty':
|
||||
return {
|
||||
...state,
|
||||
properties: Object.keys(state.properties).reduce((acc, key) => {
|
||||
if (key !== action.propertyName) acc[key] = state.properties[key];
|
||||
return acc;
|
||||
}, {}),
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Identifying the problems
|
||||
|
||||
While the code in the example is not that complicated right now, complexity can increase very fast as more action types need to be handled by our application. This is due to the fact that each `action.type`'s logic is nested inside the `reducer` function, thus adding more code and complexity with each new action.
|
||||
|
||||
Another issue we can identify is that each `action` has a different structure, which increases cognitive load for future maintainers, as they have to remember what keys their `action` needs to have. There's also the added issue of running into a case where `action.type` might be needed to pass actual data to the state (i.e. `state.type` could exist).
|
||||
|
||||
Finally, our `action.type` values are hardcoded inside the `reducer` function, making it hard to remember and sync across other files and components. This might seem like the least of our problems, but it's probably the easiest one to fix, so let's start there.
|
||||
|
||||
### Define action types
|
||||
|
||||
Starting with removing the hardcoded strings for each of the `action.type` values, we can make the code more maintainable and easier to read by extracting them to an object:
|
||||
|
||||
```js
|
||||
const ACTION_TYPES = {
|
||||
CREATE_ID: 'createID',
|
||||
SET_NAME: 'setName',
|
||||
ADD_PROPERTY: 'addProperty',
|
||||
REMOVE_PROPERTY: 'removeProperty'
|
||||
};
|
||||
```
|
||||
|
||||
### Create a common action structure
|
||||
|
||||
Our `action` objects aren't consistent in terms of structure with the exception of sharing a `type` key which we use to identify each action. If we hope to reduce mental strain and minimize headaches, we should make these more consistent. The easiest way to do so would be to put the whole action `payload` under a top-level key and nest any values passed to the action inside it:
|
||||
|
||||
```js
|
||||
// Structure of any action passed to our reducer function
|
||||
const action = {
|
||||
// Any of the previously defined action types
|
||||
type: ACTION_TYPES.CREATE_ID,
|
||||
// Nest name, propertyValue and propertyKey inside this object
|
||||
payload: { /* ... */ }
|
||||
}
|
||||
```
|
||||
|
||||
If you plug it into the previous code right away, it might seem counter-intuitive at first, but bear with me for a minute. It will all come together soon.
|
||||
|
||||
### Extract nested logic
|
||||
|
||||
Finally, we are ready to implement the most drastic fix which the previous two changes will help us facilitate - extracting nested logic. The first issue we identified was that each `action.type`'s logic was nested inside the `reducer` function. We can fix that by moving each `case` into its own function:
|
||||
|
||||
```js
|
||||
const createID = state => ({
|
||||
...state,
|
||||
id: generateID(),
|
||||
});
|
||||
|
||||
const setName = (state, { name }) => ({
|
||||
...state,
|
||||
name,
|
||||
});
|
||||
|
||||
const addProperty = (state, { propertyName, propertyValue }) => ({
|
||||
...state,
|
||||
[propertyName]: propertyValue,
|
||||
});
|
||||
|
||||
const removeProperty = (state, { propertyName }) => {
|
||||
const properties = Object.keys(state.properties).reduce((acc, key) => {
|
||||
if (key !== propertyName) acc[key] = state.properties[key];
|
||||
return acc;
|
||||
}, {});
|
||||
return { ...state, properties };
|
||||
};
|
||||
```
|
||||
|
||||
Each function has a single responsibility. Any complexity associated with each `action.type` is now part of a function responsible for that specific action type. Testing these smaller functions is a lot easier now, as they are focused on a single task, instead of being nested into a larger, more complex `reducer`.
|
||||
|
||||
### Putting it all together
|
||||
|
||||
Having implemented the above changes, let's take a look at what our final code looks like:
|
||||
|
||||
```js
|
||||
const initialState = {
|
||||
id: null,
|
||||
name: '',
|
||||
properties: {},
|
||||
};
|
||||
|
||||
const ACTION_TYPES = {
|
||||
CREATE_ID: 'createID',
|
||||
SET_NAME: 'setName',
|
||||
ADD_PROPERTY: 'addProperty',
|
||||
REMOVE_PROPERTY: 'removeProperty'
|
||||
};
|
||||
|
||||
const generateID = () => Math.floor(Math.random() * 1000);
|
||||
|
||||
const createID = state => ({
|
||||
...state,
|
||||
id: generateID(),
|
||||
});
|
||||
|
||||
const setName = (state, { name }) => ({
|
||||
...state,
|
||||
name,
|
||||
});
|
||||
|
||||
const addProperty = (state, { propertyName, propertyValue }) => ({
|
||||
...state,
|
||||
[propertyName]: propertyValue,
|
||||
});
|
||||
|
||||
const removeProperty = (state, { propertyName }) => {
|
||||
const properties = Object.keys(state.properties).reduce((acc, key) => {
|
||||
if (key !== propertyName) acc[key] = state.properties[key];
|
||||
return acc;
|
||||
}, {});
|
||||
return { ...state, properties };
|
||||
};
|
||||
|
||||
const reducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case TYPES.CREATE_ID:
|
||||
return createId(state, action.payload);
|
||||
case TYPES.SET_NAME:
|
||||
return setName(state, action.payload);
|
||||
case TYPES.ADD_PROPERTY:
|
||||
return addProperty(state, action.payload);
|
||||
case TYPES.REMOVE_PROPERTY:
|
||||
return removeProperty(state, action.payload);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
```
|
||||
55
snippets/react/s/rendering-basics.md
Normal file
55
snippets/react/s/rendering-basics.md
Normal file
@ -0,0 +1,55 @@
|
||||
---
|
||||
title: React rendering basics
|
||||
type: story
|
||||
language: react
|
||||
tags: [render]
|
||||
author: chalarangelo
|
||||
cover: comic-glasses
|
||||
excerpt: Take a deeper dive into React's rendering process and understand the basics behind the popular JavaScript framework.
|
||||
dateModified: 2021-06-12T19:30:41+03:00
|
||||
---
|
||||
|
||||
#### React rendering
|
||||
|
||||
- React rendering basics (this blog post)
|
||||
- [React rendering optimization](/blog/s/react-rendering-optimization)
|
||||
- [React rendering state](/blog/s/react-rendering-state)
|
||||
|
||||
|
||||
### Rendering introduction
|
||||
|
||||
**Rendering** is the process during which React moves down the component tree starting at the root, looking for all the components flagged for update, asking them to describe their desired UI structure based on the current combination of `props` and `state`. For each flagged component, React will call its `render()` method (for class components) or `FunctionComponent()` (for function components), and save the output produced after converting the JSX result into a plain JS object, using `React.createElement()`.
|
||||
|
||||
After collecting the render output from the entire component tree, React will diff the new tree (the **virtual DOM**) with the current DOM tree and collect the list of changes that need to be made to the DOM to produce the desired UI structure. After this process, known as **reconciliation**, React applies all the calculated changes to the DOM.
|
||||
|
||||
### Render and commit phases
|
||||
|
||||
Conceptually, this work is divided into two phases:
|
||||
|
||||
- **Render phase**: rendering components, calculating changes
|
||||
- **Commit phase**: applying the changes to the DOM
|
||||
|
||||
After the **commit phase** is complete, React will run `componentDidMount` and `componentDidUpdate` lifecycle methods, as well as `useLayoutEffect()` and, after a short timeout, `useEffect()` hooks.
|
||||
|
||||
Two key takeaways here are the following:
|
||||
|
||||
- Rendering is not the same as updating the DOM
|
||||
- A component may be rendered without any visible changes
|
||||
|
||||
### Rendering reasons
|
||||
|
||||
After the initial render has completed, there are a few different things that will cause a re-render:
|
||||
|
||||
- `this.setState()` (class components)
|
||||
- `this.forceUpdate()` (class components)
|
||||
- `useState()` setters (function components)
|
||||
- `useReducer()` dispatches (function components)
|
||||
- `ReactDOM.render()` again (on the root component)
|
||||
|
||||
### Rendering behavior
|
||||
|
||||
React's default behavior is to **recursively render all child components inside of it when a parent component is rendered**. This means that it does not care if a component's `props` have changed - as long as the parent component rendered, its children will render unconditionally.
|
||||
|
||||
To put this another way, calling `setState()` in the root component without any other changes, will cause React to re-render every single component in the component tree. Most likely, most of the components will return the exact same render output as the last render, meaning React will not have to make any changes to the DOM, but the rendering and diffing calculations will be performed regardless, taking time and effort.
|
||||
|
||||
[Continue on React rendering optimization](/blog/s/react-rendering-optimization)
|
||||
48
snippets/react/s/rendering-optimization.md
Normal file
48
snippets/react/s/rendering-optimization.md
Normal file
@ -0,0 +1,48 @@
|
||||
---
|
||||
title: React rendering optimization
|
||||
type: story
|
||||
language: react
|
||||
tags: [render]
|
||||
author: chalarangelo
|
||||
cover: comic-glasses
|
||||
excerpt: Take a deeper dive into React's rendering process and understand how to make small yet powerful tweaks to optimize performance.
|
||||
dateModified: 2021-06-12T19:30:41+03:00
|
||||
---
|
||||
|
||||
#### React rendering
|
||||
|
||||
- [React rendering basics](/blog/s/react-rendering-basics)
|
||||
- React rendering optimization (this blog post)
|
||||
- [React rendering state](/blog/s/react-rendering-state)
|
||||
|
||||
### Optimization opportunities
|
||||
|
||||
As we've seen in the [previous blog post](/blog/s/react-rendering-basics), **rendering** is React's way of knowing if it needs to make changes in the DOM, but there are certain cases where work and calculations performed during the **render phase** can be a wasted effort. After all, if a component's render output is identical, there will be no DOM updates, thus the work wasn't necessary.
|
||||
|
||||
Render output should always be based on the current combination of `props` and `state`, so it is possible to know ahead of time if a component's render output will be the same so long as its `props` and `state` remain unchanged. This is the key observation on top of which optimizing React rendering is based, as it hinges on our code doing less work and skipping component rendering when possible.
|
||||
|
||||
### Optimization techniques
|
||||
|
||||
React offers a handful of APIs that allow us to optimize the rendering process:
|
||||
|
||||
- `shouldComponentUpdate` (class components): Lifecycle method, called before rendering, returning a boolean (`false` to skip rendering, `true` to proceed as usual). Logic can vary as necessary, but the most common case is checking if the component's `props` and `state` have changed.
|
||||
- `React.PureComponent` (class components): Base class that implements the previously described `props` and `state` change check in its `shouldComponentUpdate` lifecycle method.
|
||||
- `React.memo()` (any component): Higher-order component (HOC) that wraps any given component. It implements the same kind of functionality as `React.PureComponent`, but can also wrap function components.
|
||||
|
||||
All of these techniques use **shallow equality** for comparisons. Skipping rendering a component means skipping the default recursive behavior of rendering children, effectively skipping the whole subtree of components.
|
||||
|
||||
### Reference memoization
|
||||
|
||||
Passing new references as `props` to a child component doesn't usually matter, as it will re-render regardless when the parent changes. However, if you are trying to optimize a child component's rendering by checking if its `props` have changed, passing new references will cause a render. This behavior is ok if the new references are updated data, but if it's a new reference to the same callback function passed down by the parent, it's rather problematic.
|
||||
|
||||
This is less of an issue in class components, as they have instance methods whose references don't change, although any sort of generated callbacks passed down to a component's children can result in new references. As far as function components are concerned, React provides the `useMemo()` hook for memoizing values, and the `useCallback()` hook specifically for memoizing callbacks.
|
||||
|
||||
`useMemo()` and `useCallback()` can provide performance benefits but, as with any other memoization usage, it's important to think about their necessity and the net benefit they provide in the long run. A good rule of thumb is to consider using them for pure functional components that re-render often with the same `props` and/or might do heavy calculations and avoid them elsewhere.
|
||||
|
||||
### Performance measurement
|
||||
|
||||
**React Developer Tools** provide a handy **Profiler** tab that allows you to visualize and explore the rendering process of your React applications. Under this tab, you will find a settings icon which will allow you to _Highlight updates when components render_, as well as _Record why each component rendered while profiling_ - I highly suggest ticking both of them. Recording the initial render and re-renders of the website can provide invaluable insights about the application's bottlenecks and issues and also highlight optimization opportunities (often using one of the techniques described above).
|
||||
|
||||
Finally, remember that React's development builds are significantly slower than production builds, so take all the measurements you see with a grain of salt as absolute times in development are not a valuable metric. Identifying unnecessary renders, memoization and optimization opportunities, as well as potential bottlenecks is where you should focus.
|
||||
|
||||
[Continue on React rendering state](/blog/s/react-rendering-state)
|
||||
47
snippets/react/s/rendering-state.md
Normal file
47
snippets/react/s/rendering-state.md
Normal file
@ -0,0 +1,47 @@
|
||||
---
|
||||
title: React rendering state
|
||||
type: story
|
||||
language: react
|
||||
tags: [render]
|
||||
author: chalarangelo
|
||||
cover: comic-glasses
|
||||
excerpt: Take a deeper dive into React's rendering process and understand the role of the Context API and Redux in it.
|
||||
dateModified: 2021-06-12T19:30:41+03:00
|
||||
---
|
||||
|
||||
#### React rendering
|
||||
|
||||
- [React rendering basics](/blog/s/react-rendering-basics)
|
||||
- [React rendering optimization](/blog/s/react-rendering-optimization)
|
||||
- React rendering state (this blog post)
|
||||
|
||||
### Context API
|
||||
|
||||
React's **Context API** provides a way to pass data through the component tree without using `props`, but should not be used for state management as it requires manual updating. Any component inside a context's `Provider` can access the data in the context instance using a `Consumer` component or, for function components only, the `useContext()` hook.
|
||||
|
||||
When a new reference is passed to a context `Provider` it will cause any connected components to update. React will look for any components consuming the context in the component tree and update them to reflect the change in the context's value. Passing a new object to a context `Provider` is essentially a new reference, as the context holds a single value (in this case an object).
|
||||
|
||||
### Context optimization
|
||||
|
||||
By default, any update to a parent component that renders a context `Provider` will cause all of the child components to re-render regardless of changes in the context, due to React's rendering process. To avoid re-rendering child components when a parent changes, **memoization** can be used, which will cause React to skip the whole subtree of a skipped component.
|
||||
|
||||
When the context is updated, React additionally checks for components consuming the context down the subtree. This allows context-consuming components under a memoized parent that does not re-render to consume the updated context and render as necessary. After a context-consuming component re-renders, React will keep on recursively rendering its child components as usual.
|
||||
|
||||
Oftentimes, it's a good idea to memoize the component immediately under a context `Provider`. That way updates to the parent component will not cause a re-render for the whole subtree, but only the components that consume the context.
|
||||
|
||||
### React-Redux
|
||||
|
||||
React-Redux provides bindings for **Redux**, a state container for JavaScript applications, and works a little differently from React's Context API. One of the key differences is that React-Redux only re-renders components that need to render, due to the fact that components subscribed to the Redux store read the latest store state, diff the values and force re-render only if the relevant data has changed, while React is not involved at all in the subscription callback process.
|
||||
|
||||
While this most likely means that fewer components will have to re-render compared to using a context, React-Redux always executes its `mapStateToProps` and `useSelector` functions for every connected component in the tree whenever the store state is updated. These calculations are usually less expensive than React's rendering, but if there are costly calculations performed or new references returned when they shouldn't, it might become problematic.
|
||||
|
||||
### React-Redux optimization
|
||||
|
||||
React-Redux provides two ways of connecting to its store, performing the necessary work and returning the combined `props`:
|
||||
|
||||
- `connect` (any component): Higher-order component (HOC) that wraps any given component
|
||||
- `useSelector` (function components): Hook called inside function components
|
||||
|
||||
`connect` acts a lot like memoizing a React component (i.e. using `React.PureComponent` or `React.memo()`), updating the wrapped component only when the combined `props` have changed. This means that passing new references from the parent or the passed functions will still cause a re-render. Components wrapped with `connect` usually read smaller pieces of data from the store state, are less likely to re-render due to that and usually affect fewer components down their tree.
|
||||
|
||||
On the other hand, `useSelector` has no way of stopping a component from rendering when its parent component renders. When exclusively using `useSelector`, larger parts of the component tree will re-render due to Redux store updates than they would with `connect`, since there aren't other components using `connect` to prevent them from doing so. You can use `React.memo()` as necessary, to optimize this behavior by preventing unnecessary re-rendering.
|
||||
61
snippets/react/s/selected-option.md
Normal file
61
snippets/react/s/selected-option.md
Normal file
@ -0,0 +1,61 @@
|
||||
---
|
||||
title: How can I set the value of a select input in React?
|
||||
shortTitle: Select input value
|
||||
type: question
|
||||
language: react
|
||||
tags: [components,input]
|
||||
cover: two-doors
|
||||
excerpt: Learn of all the different ways to set the value of a selected input in React with this quick guide.
|
||||
dateModified: 2021-06-12T19:30:41+03:00
|
||||
---
|
||||
|
||||
### Adding selected to an option
|
||||
|
||||
A very common way of setting a `<select>` input's value is by adding a `selected` attribute to one of its `<option>` elements. For example:
|
||||
|
||||
```jsx
|
||||
const Select = ({ values, callback, selected }) => {
|
||||
return (
|
||||
<select
|
||||
disabled={disabled}
|
||||
readOnly={readonly}
|
||||
onChange={({ target: { value } }) => callback(value)}
|
||||
>
|
||||
{values.map(([value, text]) => (
|
||||
<option selected={selected === value} value={value}>
|
||||
{text}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Setting value for the select
|
||||
|
||||
While this approach closely resembles HTML and feels intuitive, there is an easier way to do the same thing. [React](https://reactjs.org/docs/forms.html#the-select-tag) provides us with a shared API between `<input type="text">`, `<textarea>` and `<select>` where we can use `value` or `defaultValue` (depending if the input is controlled or not) to set the field's value.
|
||||
|
||||
Using this API, we minimize the effort of checking for the selected value, as well as making the code easier to read and update as necessary. Here's an example:
|
||||
|
||||
```jsx
|
||||
const Select = ({ values, callback, selected }) => {
|
||||
return (
|
||||
<select
|
||||
disabled={disabled}
|
||||
readOnly={readonly}
|
||||
defaultValue={selected}
|
||||
onChange={({ target: { value } }) => callback(value)}
|
||||
>
|
||||
{values.map(([value, text]) => (
|
||||
<option value={value}>
|
||||
{text}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Note that the above implementation uses `defaultValue`, therefore it implies that the component is uncontrolled. You can convert this `Select` component into a controlled component by using `value` instead of `defaultValue`.
|
||||
|
||||
For a more detailed explanation of the component, as well as usage examples, you can check out the [Select component](/react/s/select).
|
||||
53
snippets/react/s/use-effect-primitive-dependencies.md
Normal file
53
snippets/react/s/use-effect-primitive-dependencies.md
Normal file
@ -0,0 +1,53 @@
|
||||
---
|
||||
title: "Tip: Prefer primitives in useEffect dependencies"
|
||||
shortTitle: Primitive useEffect dependencies
|
||||
type: tip
|
||||
language: react
|
||||
tags: [hooks,effect,object,comparison]
|
||||
author: chalarangelo
|
||||
cover: automaton
|
||||
excerpt: Avoid unnecessary re-runs by using primitive dependencies in your React effect.
|
||||
dateModified: 2022-06-12T05:00:00-04:00
|
||||
---
|
||||
|
||||
Conditionally firing an effect is a pretty common requirement when working with React. In most cases, this is as simple as passing a dependency array to `useEffect()`. Turns out that, in some cases, this can cause unnecessary performance degradation.
|
||||
|
||||
Such issues arise when entire objects are passed as dependencies when you only need a primitive value. Due to the nature of objects, [being pass-by-reference](/js/s/pass-by-reference-or-pass-by-value), comparing the dependencies will always trigger a re-run. This ends up causing effects to run unnecessarily.
|
||||
|
||||
```jsx
|
||||
const CartItem = ({ item }) => {
|
||||
useEffect(() => {
|
||||
console.log(`${item.name} quantity has changed to ${item.quantity}.`);
|
||||
}, [item]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h3>{item.name}</h3>
|
||||
<span>
|
||||
{item.price} x {item.quantity}
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
The solution is to use only the parts of the object that the effect relies on. Oftentimes, these are primitives, such as strings and numbers. Comparing their values to the previous dependencies is more efficient and avoids unnecessary re-runs.
|
||||
|
||||
```jsx
|
||||
const CartItem = ({ item }) => {
|
||||
const { name, quantity, price } = item;
|
||||
|
||||
useEffect(() => {
|
||||
console.log(`${name} quantity has changed to ${quantity}.`);
|
||||
}, [name, quantity]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h3>{name}</h3>
|
||||
<span>
|
||||
{price} x {quantity}
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
69
snippets/react/s/use-interval-explained.md
Normal file
69
snippets/react/s/use-interval-explained.md
Normal file
@ -0,0 +1,69 @@
|
||||
---
|
||||
title: Write a useInterval hook in React
|
||||
shortTitle: Step-by-step useInterval hook
|
||||
type: story
|
||||
language: react
|
||||
tags: [hooks,effect]
|
||||
author: chalarangelo
|
||||
cover: clock
|
||||
excerpt: Wrapping your mind around React hooks and how they interact with `setInterval()` can be difficult. Here's a guide to get you started.
|
||||
dateModified: 2021-09-28T19:59:51+03:00
|
||||
---
|
||||
|
||||
Wrapping your mind around React hooks can be daunting at first, especially if you stumble into anything remotely related to timing, such as `setInterval()`. In order to solve such issues, you have to get used to the way hooks work, their limitations and potential workarounds.
|
||||
|
||||
First, it should be clear that `setInterval()` is a side effect. After all, it's not directly tied to a component's render method. Therefore we should call it inside a `useEffect()` hook and use its `return` to call `clearInterval()` when unmounting. To avoid creating multiple intervals, we can use the hook's second argument to pass an empty dependency array (`[]`). This results in running the side effect only when the component mounts.
|
||||
|
||||
```jsx
|
||||
React.useEffect(() => {
|
||||
let id = setInterval(callback, delay);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
```
|
||||
|
||||
The closure inside `setInterval()` will only ever have access to whatever variables and values were available when it got instantiated. This means we have to be extra careful about its first argument to make sure that fresh values will be available every time the interval runs. The easiest solution to this issue is to create a variable that's considered mutable by React, using the `useRef()` hook. This will allow us to have access to new values when we need them.
|
||||
|
||||
```jsx
|
||||
const savedCallback = React.useRef(callback);
|
||||
|
||||
React.useEffect(() => {
|
||||
let id = setInterval(savedCallback.current, delay);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
```
|
||||
|
||||
Using the `useRef()` hook might have just shifted the problem. The value of the created ref now needs to be refreshed inside `setInterval()`. Luckily, this is an easy problem to solve. We could create a wrapper function that passes the function to `setInterval()` instead. This way the function passed to `setInterval()` will never change, but the value of the enclosed ref will always be up to date when it's called.
|
||||
|
||||
```jsx
|
||||
const savedCallback = React.useRef(callback);
|
||||
|
||||
React.useEffect(() => {
|
||||
function tick() {
|
||||
savedCallback.current();
|
||||
}
|
||||
let id = setInterval(tick, delay);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
```
|
||||
|
||||
Finally, let's extract all of this into a custom hook to make it reusable. We can extract `callback` as an argument for the custom hook and use it as the sole dependency of an additional `useEffect()` hook that will update the ref for the callback.
|
||||
|
||||
```jsx
|
||||
const useInterval = callback => {
|
||||
const savedCallback = React.useRef();
|
||||
|
||||
React.useEffect(() => {
|
||||
savedCallback.current = callback;
|
||||
}, [callback]);
|
||||
|
||||
React.useEffect(() => {
|
||||
function tick() {
|
||||
savedCallback.current();
|
||||
}
|
||||
let id = setInterval(tick, delay);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
};
|
||||
```
|
||||
|
||||
That's pretty much it. With a little bit of extra effort, we can add `delay` to the arguments of our custom hook and have a complete hook version of `setInterval()`. You can find an implementation of the hook with this final adjustment, as well as some usage examples in the [useInterval snippet](/react/s/use-interval).
|
||||
36
snippets/react/s/use-state-with-label.md
Normal file
36
snippets/react/s/use-state-with-label.md
Normal file
@ -0,0 +1,36 @@
|
||||
---
|
||||
title: "Tip: Label your useState values in React developer tools"
|
||||
shortTitle: Labelling useState values
|
||||
type: tip
|
||||
language: react
|
||||
tags: [hooks]
|
||||
author: chalarangelo
|
||||
cover: bunny-poster
|
||||
excerpt: Multiple `useState` hooks in React can complicate things while debugging. Luckily, there's an easy way to label these values.
|
||||
dateModified: 2021-11-07T16:34:37+03:00
|
||||
---
|
||||
|
||||
When working with multiple `useState()` hooks in React, things can get a bit complicated while debugging. Luckily, there's an easy way to label these values, using the `useDebugValue()` hook to create a custom `useStateWithLabel` hook:
|
||||
|
||||
```jsx
|
||||
const useStateWithLabel = (initialValue, label) => {
|
||||
const [value, setValue] = useState(initialValue);
|
||||
useDebugValue(`${label}: ${value}`);
|
||||
return [value, setValue];
|
||||
};
|
||||
|
||||
const Counter = () => {
|
||||
const [value, setValue] = useStateWithLabel(0, 'counter');
|
||||
return (
|
||||
<p>{value}</p>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<Counter />
|
||||
);
|
||||
// Inspecting `Counter` in React developer tools will display:
|
||||
// StateWithLabel: "counter: 0"
|
||||
```
|
||||
|
||||
This hook is obviously meant mainly for development, but it can also be useful when creating React component or hook libraries. Additionally, you can easily abstract it in a way that the label is ignored in production builds. An example would be exporting a hook that defaults back to `useState()` when building for a production environment.
|
||||
Reference in New Issue
Block a user