React DOM

Rendering Responses

The createRouterComponent function is used to create the component at the root of a Curi + React application. You can call this component anything that you want, but here it will be referred to as the Router.

Note:

Why does @curi/react-dom export a function to create a component and not just a component? Props signify values that can change, but an application should only ever have one router. By hard-coding the router into a component, we avoid having to handle the possibility of switching routers (which should not happen).

createRouterComponent is passed the application's Curi router to create a Router component. The Router will automatically add an observer to the Curi router when it mounts, so that it can re-render when there are new responses.

Along with setting up an observer to react to new responses, the Router sets up contexts for routing values. Theresponse and navigation can be read using the useResponse hook, while the router can be read using the useRouter hook.

import { createRouterComponent, useResponse } from '@curi/react-dom';

import router from "./router";
const Router = createRouterComponent(router);

function App() {
  const {
    response,
    navigation
  } = useResponse();
  const { body:Body } = response;
  return <Body />
}

// router.once() is used to delay rendering in case
// the initially matched route is asynchronous
router.once(() => {
  ReactDOM.render((
    <Router>
      <App />
    </Router>
  ), document.getElementById("root"));
});

What to render

The Router component sets up the application's routing, while its children render the application's content. The Curi router generates response objects from matched locations; those are core for figuring out what to render.

If you use route.respond to set React components as the body properties on your responses, you can create a React element for the body component.

The Body element (it is useful to rename the response's body to Body for JSX transformation) is a placeholder for the "real" component that you render for a route. This means that the "real" component will be different for every route.

While not a strict requirement, it is useful to pass the response object as a prop to the rendered Body component.

function App() {
  const { response } = useResponse();
  const { body:Body } = response;
  return <Body response={response} />
}

ReactDOM.render((
  <Router>
    <header>
      <NavLinks />
    </header>
    <main>
      <App />
    </main>
  </Router>
), document.getElementById("root"));

If your routes use an object to attach multiple components to a response, the children function also provides a good place to split these apart.

Note:

If you do take this approach, please remember that you want every route to set the same body shape. Otherwise, you'll have to determine the shape and change how you render in the children function, which can quickly become messy.

const routes = prepareRoutes({
  routes: [
    {
      name: "Home",
      path: "",
      respond() {
        return {
          body: {
            Main: HomeMain,
            Menu: HomeMenu
          }
        }
      }
    },
    // ...
  ]
});

function App() {
  const { response } = useResponse();
  const { Main, Menu } = response.body;
  return (
    <React.Fragment>
      <header>
        <Menu />
      </header>
      <main>
        <Main response={response} />
      </main>
    </React.Fragment>
  );
}

Accessibility

Managing the application's focus when navigating is useful for users who use screen readers. The useNavigationFocus hookprovides a convenient way to focus a page's main content when it renders a new response.

You can read some more about accessibility in the accessibility guide.

import { useResponse, useNavigationFocus } from "@curi/react-dom";

function App()
  const { response } = useResponse();
  const ref = React.createRef(null);
  useNavigationFocus(ref);

  const { body:Body } = response;
  return (
    <React.Fragment>
      <header>
        <NavLinks />
      </header>
      <main ref={ref} tabIndex={-1}>
        <Body response={response} />
      </main>
    </React.Fragment>
  );
}

Please check out the full @curi/react-dom API documentation to see every component that the package provides.