Forwarding a ref to all pages (route components) just to apply it to the <main> and have a Skip Navigation Link (button). Is there no better way?

huangapple go评论83阅读模式
英文:

Forwarding a ref to all pages (route components) just to apply it to the <main> and have a Skip Navigation Link (button). Is there no better way?

问题

我已经在研究useRef和forwardRef一段时间了,只为能够将"跳过导航链接"功能添加到我的React项目中。由于多个来源都说getElementById和querySelector不是正确的做法,我最终将一个ref传递给了我的所有路由。然后在每个页面上,我将该ref进一步传递给RefMain组件。这似乎对于一个小的辅助功能来说有点过于复杂。

在每个页面中:

最后:

这当然是一个简化的示例,只包含了少量页面。看起来传递了ref的路由,远非干净。我是否漏掉了一些巧妙的技巧?我尝试在RefMain组件中使用useRef,但这使我无法将ref传递给树中更高的处理程序函数(App.js)。

英文:

I have been on it for a while now, researching useRef, forwardRef just to be able to add the Skip Navigation Link to my React project. Since multiple sources say that ElementById and querySelector are not the correct way to do it, I ended up forwarding a ref to ALL my routes. Then on each page I forward the ref further to a RefMain component. It all seems like a massive complication for a small accessibility feature.

const App = () =&gt; {
  const beginningOfMainRef = useRef(null);

  const handleFocusMain = () =&gt; beginningOfMainRef.current.focus();

  return (
    &lt;Theme&gt;
      &lt;GlobalStyle /&gt;
      &lt;SkipNavigationButton onClick={handleFocusMain}&gt;
        Skip Navigation Links
      &lt;/SkipNavigationButton&gt;
      &lt;Header /&gt;
      &lt;Routes&gt;
        &lt;Route path=&quot;/&quot; element={&lt;HomePage ref={beginningOfMainRef} /&gt;} /&gt;
        &lt;Route
          path=&quot;/distributors&quot;
          element={&lt;DistributorsPage ref={beginningOfMainRef} /&gt;}
        /&gt;
        &lt;Route
          path=&quot;/contacts&quot;
          element={&lt;ContactsPage ref={beginningOfMainRef} /&gt;}
        /&gt;
      &lt;/Routes&gt;
      &lt;Footer /&gt;
    &lt;/Theme&gt;
  );
};
export default App;

Than in each page:

const AboutPage = forwardRef((props, ref) =&gt; {
  return (
    &lt;RefMain ref={ref}&gt;
      &lt;h1&gt;hi, i&#39;m about&lt;/h1&gt;
    &lt;/RefMain&gt;
  );
});
export default AboutPage;

And finally:

const RefMain = forwardRef((props, ref) =&gt; {
  return (
    &lt;Main&gt;
      &lt;BeginningOfMain tabIndex=&quot;-1&quot; ref={ref}&gt;
        {props.title} main content
      &lt;/BeginningOfMain&gt;
      {props.children}
    &lt;/Main&gt;
  );
});

export default RefMain;

This is of course a simplified example with a reduced number of Pages. Looking at the routes with passed ref, they are anything, but dry. Am I missing some neat trick here? I tried using useRef within the RefMain component, but that left me unable to pass the ref to the handler function higher in the tree (App.js).

答案1

得分: 1

以下是代码部分的翻译:

自从您在每个路由组件中应用了`RefMain`最好将其抽象为布局路由组件而不是每个页面内容周围的包装组件它不再呈现`children`属性而是呈现一个[`Outlet`][1]组件用于呈现嵌套路由的内容

```jsx
const RefMain = forwardRef((props, ref) => {
  const [title, setTitle] = React.useState();

  return (
    <Main>
      <BeginningOfMain tabIndex="-1" ref={ref}>
        {title} 主要内容
      </BeginningOfMain>
      <Outlet context={{ setTitle }} />
    </Main>
  );
});

export const RefWrapper = ({ children, title }) => {
  const { setTitle } = useOutletContext();
  React.useEffect(() => {
    setTitle(title);
  }, [title]);

  return children;
};

export default RefMain;
const AboutPage = () => {
  return (
    <h1>我是关于</h1>
  );
}
export default AboutPage;

当页面位置更新时,您可以使用另一个引用和div元素将焦点设置回页面顶部。

const App = () => {
  const topRef = useRef();
  const beginningOfMainRef = useRef(null);

  const location = useLocation();

  useEffect(() => {
    topRef.current.scrollIntoView();
    topRef.current.focus();
  }, [location.pathname]);

  const handleFocusMain = () => beginningOfMainRef.current.focus();

  return (
    <Theme>
      <div ref={topRef} tabIndex={-1} />
      <GlobalStyle />
      <SkipNavigationButton onClick={handleFocusMain}>
        跳过导航链接
      </SkipNavigationButton>
      <Header />
      <Routes>
        <Route element={<RefMain ref={beginningOfMainRef} />}>
          <Route path="/" element={<HomePage />} />
          <Route
            path="/distributors"
            element={(
              <RefWrapper title="分销商">
                <DistributorsPage />
              </RefWrapper>
            )}
          />
          <Route
            path="/contacts"
            element={(
              <RefWrapper title="联系人">
                <ContactsPage />
              </RefWrapper>
            )}
          />
        </Route>
      </Routes>
      <Footer />
    </Theme>
  );
};

export default App;

我没有创建任何沙盒来测试这个,但我相信它应该接近您所描述的期望行为。如果有问题,请告诉我,我会制作一个实时演示。


<details>
<summary>英文:</summary>

Since you apply `RefMain` in each routed component it would better to abstract it as a layout route component instead of a wrapper component around each page&#39;s content. Instead of rendering the `children` prop it renders an [`Outlet`][1] component for nested routes to render their content into.

```jsx
const RefMain = forwardRef((props, ref) =&gt; {
  const [title, setTitle] = React.useState();

  return (
    &lt;Main&gt;
      &lt;BeginningOfMain tabIndex=&quot;-1&quot; ref={ref}&gt;
        {title} main content
      &lt;/BeginningOfMain&gt;
      &lt;Outlet context={{ setTitle }} /&gt;
    &lt;/Main&gt;
  );
});

export const RefWrapper = ({ children, title }) =&gt; {
  const { setTitle } = useOutletContext();
  React.useEffect(() =&gt; {
    setTitle(title);
  }, [title]);

  return children;
};

export default RefMain;
const AboutPage = () =&gt; {
  return (
    &lt;h1&gt;hi, i&#39;m about&lt;/h1&gt;
  );
}
export default AboutPage;

You can use another ref and div element to set the focus back to the top of the page when the page location updates.

const App = () =&gt; {
  const topRef = useRef();
  const beginningOfMainRef = useRef(null);

  const location = useLocation();

  useEffect(() =&gt; {
    topRef.current.scrollIntoView();
    topRef.current.focus();
  }, [location.pathname]);

  const handleFocusMain = () =&gt; beginningOfMainRef.current.focus();

  return (
    &lt;Theme&gt;
      &lt;div ref={topRef} tabIndex={-1} /&gt;
      &lt;GlobalStyle /&gt;
      &lt;SkipNavigationButton onClick={handleFocusMain}&gt;
        Skip Navigation Links
      &lt;/SkipNavigationButton&gt;
      &lt;Header /&gt;
      &lt;Routes&gt;
        &lt;Route element={&lt;RefMain ref={beginningOfMainRef} /&gt;}&gt;
          &lt;Route path=&quot;/&quot; element={&lt;HomePage /&gt;} /&gt;
          &lt;Route
            path=&quot;/distributors&quot;
            element={(
              &lt;RefWrapper title=&quot;Distributors&quot;&gt;
                &lt;DistributorsPage /&gt;
              &lt;/RefWrapper&gt;
            )}
          /&gt;
          &lt;Route
            path=&quot;/contacts&quot;
            element={(
              &lt;RefWrapper title=&quot;Contacts&quot;&gt;
                &lt;ContactsPage /&gt;
              &lt;/RefWrapper&gt;
            )}
          /&gt;
        &lt;/Route&gt;
      &lt;/Routes&gt;
      &lt;Footer /&gt;
    &lt;/Theme&gt;
  );
};

export default App;

I didn't stand up any sandbox to test this but it is my belief it should be close to the desired behavior you describe. If there are issues let me know and I'll work on a live demo.

答案2

得分: 0

This isn't a react answer but a skip link should be very simple. They're typically the first DOM element on the page and all it does is link to a spot further down on the page. A skip link does not link to a different page.

<a href="#skip">skip to main</a>
...
<main id="skip" tabindex="-1">
  stuff on the page
</main>

It all seems like a massive complication for a small accessibility feature.

你是否将个人喜好应用于这个的辅助功能有多有用呢?您是否使用吸气-吹气设备?是否因为不得不多次吹气以导航到顶层菜单中的所有项目,才能进入页面的主要部分而感到疲惫?

英文:

This isn't a react answer but a skip link should be very simple. They're typically the first DOM element on the page and all it does is link to a spot further down on the page. A skip link does not link to a different page.

&lt;a href=&quot;#skip&quot;&gt;skip to main&lt;/a&gt;
...
&lt;main id=&quot;skip&quot; tabindex=&quot;-1&quot;&gt;
  stuff on the page
&lt;/main&gt;

>It all seems like a massive complication for a small accessibility feature.

Are you applying your personal preference as to how useful that small accessibility feature is? Do you use a sip-and-puff device? Do you get worn down by how many times you have to puff to navigate through all the items in a top level menu just to get to the main part of the page?

huangapple
  • 本文由 发表于 2023年2月8日 15:46:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/75382686.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定