Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Hybrid implicit/explicit state #5

Open
EricRibeiro opened this issue Apr 17, 2021 · 0 comments
Open

RFC: Hybrid implicit/explicit state #5

EricRibeiro opened this issue Apr 17, 2021 · 0 comments
Labels

Comments

@EricRibeiro
Copy link
Member

I'm creating this issue to propose an enhancement over how we pass state to components. Instead of having to pass it for all components, we would only need to pass it to the top-level ones.

For example, currently we have to pass the state to all instances of MenuItem inside Menu:

const state = useMenuState();

<MenuButton {...state}>Open menu</MenuButton>
<Menu {...state}>
  <MenuArrow {...state} />
  <MenuItem {...state}>Item 1</MenuItem>
  <MenuItem {...state}>Item 2</MenuItem>
  <MenuSeparator {...state} />
  <MenuItem {...state}>Item 3</MenuItem>
</Menu>

I'm proposing that we improve this so we don't need to explicitly pass state to MenuItem instances anymore. It would leverage React Context and subscribe to state changes internally.

const state = useMenuState();

<MenuButton {...state}>Open menu</MenuButton>
<Menu {...state}>
  <MenuArrow />
  <MenuItem>Item 1</MenuItem>
  <MenuItem>Item 2</MenuItem>
  <MenuSeparator />
  <MenuItem>Item 3</MenuItem>
</Menu>

Note: MenuItem should keep accepting state being passed directly as props for this reason. But it wouldn't be required anymore.

Motivation

Besides being easier to write code like that, it would also make it easier to colocate state.

In our Menu example, all MenuItem components will re-render when state gets updated. But if we abstract the state into a separate component that receives children, they won't re-render:

import {
  useMenuState,
  Menu as BaseMenu,
  MenuButton,
  MenuItem,
} from \"reakit/Menu\";

function Menu({ button, ...props }) {
  const state = useMenuState();
  return (
    <React.Fragment>
      <MenuButton {...state} ref={button.ref} {...button.props}>
        {(buttonProps) => React.cloneElement(button, buttonProps)}
      </MenuButton>
      <BaseMenu {...state} {...props} />
    </React.Fragment>
  );
}

function App() {
  return (
    <Menu button={<button>Open menu</button>}>
      <MenuItem>Item 1</MenuItem>
      <MenuItem>Item 2</MenuItem>
      <MenuItem>Item 3</MenuItem>
      ...
      <MenuItem>Item 500</MenuItem>
    </Menu>
  );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant