反应:从外部更改组件的子代

时间:2019-01-18 12:41:52

标签: javascript reactjs

在React项目中,我有一个组件Header.js,它是全局布局的一部分,并在每个页面上使用。路由是通过 React Router v4 完成的。

Header.js组件应具有不同的子代(例如面包屑或标题),具体取决于我当前所在的页面。

示例:在HomePage.js上,我想显示面包屑。但是在AboutPage.js上,我只想有一个标题。

<App>
  <Header />

  <AnotherComponent />
  <MoreComponents />
  <BrowserRouter>
  ...
  </BrowserRouter>
</App>

我曾考虑过将Header.js设为HOC,但是由于布局和样式方面的原因,我无法将其嵌入到每个页面中。它必须位于App.js中完全相同的位置。

有什么想法/技巧吗?

编辑:理想情况下,我希望每个页面都包含Header.js的内容,就像Helmet软件包那样:

<>
  {/* HomePage.js */}
  <HeaderPortal>
    <p>Content for the header</p>
  </HeaderPortal>

  <PageContent />
</>

1 个答案:

答案 0 :(得分:1)

做到这一点的一种方法是使用React的上下文API,但是您必须在DOM中包装很多内容,以便能够从任何地方提供标头内容的设置方法和获取方法。

Header组件如下所示:

const HeaderContext = React.createContext();

// Provides the context and state - wrap app
export class HeaderProvider extends React.Component {
  state = {};
  render() {
    const { children } = this.props;
    return (
      <HeaderContext.Provider
        value={{
          header: this.state.header,
          setHeader: ({ header }) => this.setState({ header })
        }}
      >
        {children}
      </HeaderContext.Provider>
    );
  }
}
// Wrapper around WHERE the header will be rendered
export function HeaderPlacement() {
  return (
    <HeaderContext.Consumer>{({ header }) => header}</HeaderContext.Consumer>
  );
}
// Wrapper for setting the header CONTENT
export function HeaderPortal({ children }) {
  return (
    <HeaderContext.Consumer>
      {({ header, setHeader }) => 
        !header // Guard against setting the header in an endless loop
        && setHeader({ header: children })}
    </HeaderContext.Consumer>
  );
}

然后用法如下:

function App() {
  return (
    <HeaderProvider>
      <div className="App">
        <HeaderPlacement />
        <Page />
      </div>
    </HeaderProvider>
  );
}

function Page() {
  return (
    <React.Fragment>
      <HeaderPortal><h1>Page A Test</h1></HeaderPortal>
      <div>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. In ex odio,
        eleifend in consequat nec, maximus lobortis ligula. Duis elementum, diam
        et imperdiet pulvinar, nulla ligula commodo elit, ac ullamcorper justo
        mauris quis velit. 
      </div>
    </React.Fragment>
  );
}

如果多个后代尝试设置标头内容,则必须小心,因为您要么会遇到循环setState问题,要么会覆盖另一个问题。

这绝对不是唯一的方法,我避免使用钩子,但这肯定是 a React-y方法。

在此处签出代码和框:https://codesandbox.io/s/pj2q10700j