UseEffect: Failing trying to use another hook
melloware opened this issue · 5 comments
Code Sandbox: https://codesandbox.io/s/react-router-forked-mnnxb
In the above sandbox I wanted to use a useEffect
hook inside my shared hook. For example I want to write a shared hook that listens for location
changes and updated the document title like this from React Router...
import React, { useState, useEffect } from "react";
import { useBetween } from "use-between";
import { useLocation } from "react-router-dom";
const useShared = () => {
const location = useLocation();
const [value, setValue] = useState(false);
useEffect(() => {
document.title = `Location: ${location}`;
setValue(true);
}, [location]); // Only re-run the effect page location changes
return {
value,
setValue
};
};
export const useSharedHook = () => useBetween(useShared);
Is my use case wrong because I get this error:
TypeError
Invalid attempt to destructure non-iterable instance.
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.
Hi @melloware, great catch!
What to say about the error "Invalid attempt to destructure non-iterable instance."
This is because the useShared
function returns an object (https://codesandbox.io/s/react-router-forked-mnnxb?file=/components/state.js:400-439)
const useShared = () => {
...
return {
value,
setValue
};
};
And inside About
and Home
, an attempt to access it as an array:
(https://codesandbox.io/s/react-router-forked-mnnxb?file=/components/About.js:93-135)
const About = () => {
const [value, setValue] = useSharedHook();
...
Here's a working way (https://codesandbox.io/s/react-router-forked-cbz4b?file=/components/About.js:99-118)
But there is another problem:
The useLocation
hook internally uses the React context.
The error looks like this:
Hook "useContext" no possible to using inside useBetween scope.
There is no way to use the React context inside shared states, since they exist outside the React component hierarchy.
We will create a component like this (https://codesandbox.io/s/react-router-forked-cbz4b?file=/components/state.js:517-713):
export const SharedConnector = () => {
const location = useLocation(); // We can only use inside React component's, as it uses the React context internally.
const {setLocation} = useSharedHook();
useEffect(() => {
setLocation(location.pathname); // Set "location.pathname" to the shared state
}, [location]);
return null;
};
Which will forward location.pathname
inside our shared state.
And put it inside the Router
element to use its context. (https://codesandbox.io/s/react-router-forked-cbz4b?file=/index.js:309-323)
const BasicExample = () => (
<Router>
<SharedConnector />
...
But to my surprise in the final example there is a bug when updating setValue(true)
inside useEffect
, the component is not updated the first time. I will see it. (https://codesandbox.io/s/react-router-forked-cbz4b?file=/components/state.js:328-342)
Excellent explanation and example!
also for the error "Invalid attempt to destructure non-iterable instance." is there any way to catch that error and give the user a better error message about their invalid use of use-between? Because I was stumped at first on what the error was trying to tell me. :)
Hi @melloware,
I spent some time with this issue.
But to my surprise in the final example there is a bug when updating setValue(true) inside useEffect, the component is not updated the first time. I will see it. (https://codesandbox.io/s/react-router-forked-cbz4b?file=/components/state.js:328-342)
And I fixed it in version 1.3.1, you can see the result of the work here.
https://codesandbox.io/s/react-router-forked-forked-gntx4?file=/components/state.js (it's just a fork with an updated version).
Try It Out and
Happy Coding)
Nice!!!