Last active
September 2, 2021 06:30
-
-
Save AZaviruha/1d4702c5c5a190bbdbd1091d8aaaab93 to your computer and use it in GitHub Desktop.
Revisions
-
AZaviruha revised this gist
Sep 2, 2021 . 1 changed file with 6 additions and 6 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -12,7 +12,7 @@ function MyComponent () { const getData = useCallback(async (x, y) => { const data = await loadData({ x, y }) setFoo(data) }, []) useEffect(() => { @@ -33,7 +33,7 @@ function MyComponent () { const getData = useCallback(async (x, y) => { const data = await loadData({ x, y, z }) setFoo(data) }, [ /* z ?? */]) useEffect(() => { @@ -55,7 +55,7 @@ function MyComponent () { const getData = useCallback(async (x, y) => { const data = await loadData({ x, y, z: z.current }) setFoo(data) }, []) useEffect(() => { @@ -77,7 +77,7 @@ function MyComponent () { const getData = useCallback(async (x, y) => { const data = await loadData({ x, y, z: z.current }) setFoo(data) }, []) const handleOnClick = useCallback(() => { @@ -110,12 +110,12 @@ function MyComponent () { const getData = useCallback(async (x, y) => { const data = await loadData({ x, y, z: z.current }) setFoo(data) }, []) const handleOnClick = useCallback(() => { z.current = Date.now() // Will not trigger re-render forceRender() // but this will }, []) useEffect(() => { -
AZaviruha revised this gist
Sep 2, 2021 . 1 changed file with 7 additions and 9 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -22,9 +22,7 @@ function MyComponent () { ``` This all works well, until x and y is enough for `getData` to fetch data. But what if we need some additional argument (z) and at the same time we don't want to use this argument as a dependency in the `useEffect` (because changes of `z` can trigger unnecessary calls of this effect)? ```javascript function MyComponent () { @@ -34,13 +32,13 @@ function MyComponent () { const [foo, setFoo] = useState(null) const getData = useCallback(async (x, y) => { const data = await loadData({ x, y, z }) setFoo(data) }, [ /* z ?? */]) useEffect(() => { getData(x, y) }, [x, y, /* z ?? */]) } ``` @@ -94,7 +92,7 @@ function MyComponent () { <div> <button onClick={handleOnClick}>Update "z"</button> <AnotherComponent value={z.current} /> </div> ) } ``` @@ -128,12 +126,12 @@ function MyComponent () { <div> <button onClick={handleOnClick}>Update "z"</button> <AnotherComponent value={z.current} /> </div> ) } ``` Using a meaningless state setter we can force re-render, but at this moment your code will look like a combination of dirty hacks and workarounds. I don't like how it smells. It seems that right now using Redux with some middleware provides more straight-forward solution to the side-effect management. I should to admit it even despite the fact that I'm a big fan of using React without additional libraries. -
AZaviruha created this gist
Sep 2, 2021 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,139 @@ It is possible to implement side-effects in the React component without any additional tool, like Redux's middlewares (saga, thunks, etc) . It can be achieved using the combination of `useEffect` and `async\await` functions, maybe wrapped inside `useCallback` This approach is workable, but it has some pitfalls. Let's look at this example: ```javascript function MyComponent () { const [x, setX] = useState(42) const [y, setY] = useState(43) const [foo, setFoo] = useState(null) const getData = useCallback(async (x, y) => { const data = await loadData({ x, y }) setFoo(data) }, []) useEffect(() => { getData(x, y) }, [x, y]) } ``` This all works well, until x and y is enough for `getData` to fetch data. But what if we need some additional argument (z) and at the same time we don't want to use this argument as a dependency in the `useEffect` (because changes of `z` can trigger unnecessary calls of this effect)? ```javascript function MyComponent () { const [x, setX] = useState(42) const [y, setY] = useState(43) const [z, setZ] = useState(44) const [foo, setFoo] = useState(null) const getData = useCallback(async (x, y) => { const data = await loadData({ x, y, z: z.current }) setFoo(data) }, []) useEffect(() => { getData(x, y) }, [x, y]) } ``` We can't add `z` as a dependency to the `useCallack` too, because in that case we will need to add the `getData` function itself to the effect's dependencies. Without this we have a chance to call an outdated `getData` (with old value of `z` inside it). But this is not what we want. We want only `x` and `y` to be the reason of `useEffect` calls. For now I discovered only one solution for this problem: refs. ```javascript function MyComponent () { const [x, setX] = useState(42) const [y, setY] = useState(43) const [foo, setFoo] = useState(null) const z = useRef(null) const getData = useCallback(async (x, y) => { const data = await loadData({ x, y, z: z.current }) setFoo(data) }, []) useEffect(() => { getData(x, y) }, [x, y]) } ``` But to be honest, this looks more like a hack than a "proper solution". It becomes obvious when you need to use `z` as a property for some component: it will not work, because change of the ref does not trigger re-rendering of the component. So you can't do this: ```javascript function MyComponent () { const [x, setX] = useState(42) const [y, setY] = useState(43) const [foo, setFoo] = useState(null) const z = useRef(null) const getData = useCallback(async (x, y) => { const data = await loadData({ x, y, z: z.current }) setFoo(data) }, []) const handleOnClick = useCallback(() => { z.current = Date.now() // Will not trigger re-render }, []) useEffect(() => { getData(x, y) }, [x, y]) return ( <div> <button onClick={handleOnClick}>Update "z"</button> <AnotherComponent value={z.current} /> </div> ) } ``` You can solve this problem using another "dirty hack": ```javascript function MyComponent () { const [x, setX] = useState(42) const [y, setY] = useState(43) const [foo, setFoo] = useState(null) const z = useRef(null) const [, forceRender] = useState(null) const getData = useCallback(async (x, y) => { const data = await loadData({ x, y, z: z.current }) setFoo(data) }, []) const handleOnClick = useCallback(() => { z.current = Date.now() // Will not trigger re-render forceRender() // but this will }, []) useEffect(() => { getData(x, y) }, [x, y]) return ( <div> <button onClick={handleOnClick}>Update "z"</button> <AnotherComponent value={z.current} /> </div> ) } ``` Using a meaningless "setter" from the `useState` will force re-render, but at this moment your code looks like a combination of dirty hacks and workarounds. I don't like how it smells. It seems that right now using Redux with some middleware provides more straight-forward solution to the side-effect management. I should to admit it even despite the fact that I'm a big fan of using React without additional libraries.