pshrmn / curi

A JavaScript router for single-page applications
https://curi.js.org
MIT License
269 stars 10 forks source link

useRouter not updating state without useResponse #221

Closed karandwivedi42 closed 5 years ago

karandwivedi42 commented 5 years ago

Not exactly sure what is leading to this issue.

In a functional component, using useRouter without useResponse leads to the state update not propogating to component.

Code:

import React, { useEffect } from 'react';
import { Link, useRouter, useResponse } from '@curi/react-dom';

const App = () => {
  // const { response } = useResponse(); 
  const router = useRouter();
  const route = router.route(router.current().response.name);
  useEffect(() => {
    console.log('App useEffect');
  }, [route]);

  return (
    <div>
        App
        <Link name="Some Link">Link</Link>
    </div>
  )
}

export default App;

App useEffect is printed only once (component mount) and not when link is followed. If I unComment the useResponse hook call then App useEffect is also printed when following the link.

pshrmn commented 5 years ago

This is the expected behavior.

React has an internal optimization that skips re-rendering an element if its props are the same as the previous render and its context has not changed.

My assumption is that you are rendering your App like this:

ReactDOM.render((
  <Router>
    <App />
  </Router>
), root);

in which case the App has no props, so the App will only re-render when the context has changed.

Curi uses two context providers: a router provider for accessing the router and a response provider for accessing the response/navigation objects. These are kept separately so that components that only need to access the router, like a Link, aren't forced to re-render when there is a new response.

When there is a new response, the response context is updated, so any component that accesses it (e.g. one that uses useResponse) will be re-rendered.

useRouter doesn't have the same effect; when there is a new response, the router context is still the same router object.

Does this make sense? I can update the documentation if it wasn't very clear on this.

karandwivedi42 commented 5 years ago

Thanks!

I was actually using useRouter for router.current().response.name which I changed to useResponse and response.name.