betula/use-between

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!!!