preact-router
preact-router copied to clipboard
preact-router not triggering useEffect return function when navigating using route
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 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.
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]);
}
(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)
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 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
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?
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.)