preact-router icon indicating copy to clipboard operation
preact-router copied to clipboard

preact-router not triggering useEffect return function when navigating using route

Open alterx opened this issue 5 years ago • 7 comments
trafficstars

Using preact-router and the useEffect hook won't properly trigger the returned function.

For example, if we transform the Clock example from the Preact site (https://preactjs.com/guide/v10/components#class-components):

class Clock extends Component {

  constructor() {
    super();
    this.state = { time: Date.now() };
  }

  // Lifecycle: Called whenever our component is created
  componentDidMount() {
    // update time every second
    this.timer = setInterval(() => {
      this.setState({ time: Date.now() });
    }, 1000);
  }

  // Lifecycle: Called just before our component will be destroyed
  componentWillUnmount() {
    // stop when not renderable
    clearInterval(this.timer);
  }

  render() {
    let time = new Date(this.state.time).toLocaleTimeString();
    return <span>{time}</span>;
  }
}

into a "Functional Component" that uses Hooks as follows:

const ClockFn = () => {
  const [time, setTime] = useState(Date.now());

  useEffect(() => {
    const timer = setInterval(() => {
      setTime(Date.now());
      console.log(Date.now());
    }, 1000);

    return () => {
      clearInterval(timer);
      console.log('unmount');
    };
  }, []);

  return <span>{time}</span
    ><button
      onClick={() => {
        route('/');
      }}
    >
      Go home
    </button>;
};

the timer will continue to run. In the class component, on the other hand, the componentWillUnmount lifecycle method is properly executed. If supporting this is outside of the scope of preact-router please feel free to close.

Thanks in advance

alterx avatar May 04 '20 17:05 alterx

@alterx The code in the pasted snippets looks correct. It unfortunately doesn't show how your Router is set up. Can you share a bit more information? A git repo where the issue can be reproduced or a codesandbox would be best.

marvinhagemeister avatar May 04 '20 20:05 marvinhagemeister

Just a note: if you have two routes that are the same component type, preact-router won't forcibly unmount and remount that component when changing the URL. You can opt-in to this behavior using key:

<Router>
    <Home default />
    <Profile
+     key="me"
      path="/profile"
      user="me"
    />
    <Profile
+     key="profile"
      path="/profile/:user"
    />
</Router>

Alternatively, you can pass whatever parameter differentiates between your routes to useEffect():

function Profile({ user }) {
  useEffect(() => {
    /* route mounted, or got a new user value */

    return () => { /* route unmounted or user changed */ };
  }, [user]);
}

developit avatar May 09 '20 03:05 developit

(also, totally an aside here, but <button onClick={()=>route('/')}> is an antipattern and it's best to use <a href="/">. the result is the same, but <a> is more accessible)

developit avatar May 09 '20 03:05 developit

I have also encountered this behavior. With components written as objects no problem. With components written as functions, the useEffect hook is not called. I think the stupidest possible example to show this effect is this:

https://jsfiddle.net/naLrb5c7/1/

Page1 and Page2 are written respectively as an object and as a function, while the componentDidMount method is always correctly called, Page2's useEffect is never executed.

micheg avatar Jul 02 '20 07:07 micheg

@micheg Could very likely be an issue with unpkg. It's been known not to support the node resolution algorithm only partially. A more reliable cdn is pika.dev or npm.reversehttp.com.

Copying the fiddle into codesandbox shows that it's working just fine: https://codesandbox.io/s/vibrant-gates-5dq5f?file=/src/index.js

marvinhagemeister avatar Jul 02 '20 11:07 marvinhagemeister

Thanks, yes I see it working on the sandbox. In your opinion, what is the best way to use preact, routing etc without build tools?

micheg avatar Jul 02 '20 11:07 micheg

Import everything as a bundle from npm.reversehttp.com as you suggested, it works great. (In my case, https://npm.reversehttp.com/preact,preact-router,preact/hooks,htm) This can be a good way for me to use the library without build tools. thx.. (so the problem is on unpkg and not on the library.)

micheg avatar Jul 02 '20 11:07 micheg