Skip to content

Commit

Permalink
feat: add useComponentWillReceiveUpdate
Browse files Browse the repository at this point in the history
  • Loading branch information
Jack-Works committed Oct 10, 2024
1 parent 13e8fdd commit b4a39dc
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 0 deletions.
62 changes: 62 additions & 0 deletions docs/src/pages/use-component-will-receive-update.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
title: useComponentWillReceiveUpdate
---

# useComponentWillReceiveUpdate

import ExportMetaInfo from '../components/export-meta-info';

<ExportMetaInfo />

Reset part of states when props changed.

## Usage

```js
import { useComponentWillReceiveUpdate } from 'foxact/use-abortable-effect';

function Component(props) {
const [a, setA] = useState('')
const [b, setB] = useState('')
// when props.x changed, only reset a, not b
useComponentWillReceiveUpdate(() => {
setA('')
}, [props.x]);
}
```

If you're using useEffect like this:

```js
const [state, setState] = useState(false)
useEffect(() => setState(false), [props.someProp])
```

Don't do it. See [Adjusting some state when a prop changes](https://react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes)
It should be like this:
```js
const [prev, setPrev] = useState(state)
if (prev !== state) {
setPrev(state)
setState(false)
}
```

This hook is a helper for the above pattern.

```js
useComponentWillReceiveUpdate(() => setState(false), [state])
```

This only applies to states of the current component.
Modifying states from other components causes React reporting errors.
You may also want to read [(Avoid) Notifying parent components about state changes](https://react.dev/learn/you-might-not-need-an-effect#notifying-parent-components-about-state-changes)
and [(Avoid) Passing data to the parent](https://react.dev/learn/you-might-not-need-an-effect#passing-data-to-the-parent).
If you really need to edit other components' states, write it like this:

```js
useComponentWillReceiveUpdate(() => {
setLocalState(false)
Promise.resolve().then(() => props.setParentState(false))
}, [state])
```
52 changes: 52 additions & 0 deletions src/use-component-will-receive-update/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useState } from 'react';

/**
* If you're using useEffect like this:
*
* ```js
* const [state, setState] = useState(false)
* useEffect(() => setState(false), [props.someProp])
* ```
* Don't do it. See [Adjusting some state when a prop changes](https://react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes)
* It should be like this:
* ```js
* const [prev, setPrev] = useState(state)
* if (prev !== state) {
* setPrev(state)
* setState(false)
* }
* ```
* This hook is a helper for the above pattern.
* ```js
* useComponentWillReceiveUpdate(() => setState(false), [state])
* ```
*
* This only applies to states of the current component.
* Modifying states from other components causes React reporting errors.
* You may also want to read [(Avoid) Notifying parent components about state changes](https://react.dev/learn/you-might-not-need-an-effect#notifying-parent-components-about-state-changes)
* and [(Avoid) Passing data to the parent](https://react.dev/learn/you-might-not-need-an-effect#passing-data-to-the-parent).
* If you really need to edit other components' states, write it like this:
*
* ```js
* useComponentWillReceiveUpdate(() => {
* setLocalState(false)
* Promise.resolve().then(() => props.setParentState(false))
* }, [state])
* ```
*
* @param callback
* @param deps
*/
export function useComponentWillReceiveUpdate(callback: () => void, deps: readonly unknown[]) {
deps = [...deps]
const [prev, setPrev] = useState(deps)
let changed = deps.length !== prev.length
for (let i = 0; i < deps.length; i += 1) {
if (changed) break
if (prev[i] !== deps[i]) changed = true
}
if (changed) {
setPrev(deps)
callback()
}
}

0 comments on commit b4a39dc

Please sign in to comment.