React

Rendering Responses#

The curiProvider() 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).

curiProvider() is passed the application's Curi router. The returned component will automatically add an observer to the Curi router when it mounts, so that it can re-render when there are new responses.

The <Router> takes a render-invoked function as its children prop. This function will be called with an object that has three properties— response, router, and navigation—and returns the React element(s) that form the root of the application.

import { curiProvider } from '@curi/react-dom';

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

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

What to return from children()#

The render-invoked children() is responsible for rendering the root elements for an application.

If you set React components as the body properties on your responses, you can create a React element for the body component in this function.

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. When it comes to passing props to the <Body>, you could use response.name to determine what props to pass based on which route matched, but passing the same props to every route's <Body> is usually sufficient. Passing the entire response is generally useful so that the route components can access any params, data, and other properties of the response.

ReactDOM.render((
  <Router>
    {({ response, router, navigation }) => {
      // rename body to Body for JSX transformation
      const { body:Body } = response;
      return (
        <React.Fragment>
          <header>
            <NavLinks />
          </header>
          <main>
            <Body response={response} />
          </main>
        </React.Fragment>
      );
    }}
  </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.

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 = [
  {
    name: "Home",
    path: "",
    response() {
      return {
        body: {
          Main: HomeMain,
          Menu: HomeMenu
        }
      }
    }
  },
  // ...
];

ReactDOM.render((
  <Router>
    {({ response, router, navigation }) => {
      const { Main, Menu } = response.body;
      return (
        <React.Fragment>
          <header>
            <Menu />
          </header>
          <main>
            <Main response={response} />
          </main>
        </React.Fragment>
      );
    }}
  </Router>
), document.getElementById("root"));
Note:

There is a <Curious> component that you can render to access the response, router, and navigation objects anywhere* in your application. This can help prevent having to pass props through multiple layers of components.

* anywhere that is a child of your <Router>.

import { Curious } from "@curi/react-dom";
            
const BaseRouteName = ({ response }) => (
  <div>{response.name}</div>
);

export default function RouteName() {
  return (
    <Curious>
      {({ response }) => <BaseRouteName response={response} />}
    </Curious>
  );
}

Accessibility#

Managing the application's focus when navigating is useful for users who use screen readers. The <Focus> component provides 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 { Focus } from "@curi/react-dom";
            
ReactDOM.render((
  <Router>
    {({ response }) => {
      const { body:Body } = response;
      return (
        <React.Fragment>
          <header>
            <NavLinks />
          </header>
          <Focus>
            {ref => (
              <main ref={ref} tabIndex={-1}>
                <Body response={response} />
              </main>
            )}
          </Focus>
        </React.Fragment>
      );
    }}
  </Router>
), document.getElementById("root"));

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