【问题标题】:Is there a way to pass down props to a component implicitly?有没有办法将道具隐式传递给组件?
【发布时间】:2016-06-21 02:26:36
【问题描述】:

我希望能够为我的所有组件设置一个全局主题(变量集)以继承和扩展它们的默认变量。例如,我有一个 button 组件,它具有引用一组变量的默认样式(内联 CSS),例如 primaryColor,... 我希望能够在任何我使用的地方轻松更新这些变量这些组件无需显式传递给组件。

例如,我想要以下行为,我可以 (1) 将它们包装在一个组件中并将 primaryColor 级联到每个 Button 组件或 (2) 将此组件导出为更高阶的组件并提要更新道具等...但是,我无法使这些方法中的任何一种起作用。也许,有更好的方法或者...

(1)

render() {
  return (
    <Theme variables={{ 'primaryColor': 'red' }}>
      <Button />
      <SecondButton />
      <ThirdButton />
    </Theme>
  );
}

(2)

render() {
  return (
    <div>
      <Button />
      <SecondButton />
      <ThirdButton />
    </div>
  );
}

export default Theme(SampleComponent)

这种方法很有效,因为它显然是明确地传递给每个组件的:

render() {
  return (
    <div>
      <Button variables={{ 'primaryColor': 'red' }} />
      <SecondButton variables={{ 'primaryColor': 'red' }} />
      <ThirdButton variables={{ 'primaryColor': 'red' }} />
    </div>
  );
}

【问题讨论】:

  • 为什么不在你的构造函数中设置它们呢? import your_globals from 'somewhere' 然后在您的构造函数中... this.options = your_globals... 然后在渲染中您可以使用 this.options.primaryColor
  • 这些组件中的每一个都将位于它们自己的 npm 包中,因此它们无法引用其 repo 之外的一些全局。我正在考虑在 React 中使用 context 来实现这一点……不确定这种方法的可行性。
  • 好吧,你没有在你的问题中解释,哈哈。如果它们是单独的 npm 包,那么制作一个其他人使用的 npm 包可能是值得的。这意味着如果你有一个一堆默认选项和样式的包,您可以将其作为一个包并将其安装为对其他包的依赖项
  • 是的,我很抱歉!我应该更干净。我已经使用它们都继承自的全局变量模块来执行此操作,但我希望能够从使用这些组件的顶级组件中覆盖这些变量。因此,我开始使用context 功能的原因。

标签: reactjs redux higher-order-functions


【解决方案1】:

我可以看到一些你可以做到这一点的方法:

扩展子道具

有限,但允许将“额外”道具传递给组件的直接子级:

import React, { Component, Children } from 'react';

class Theme extends Component {
  getChildren () {
    const { children, variables } = this.props;
    // Clone the child components and extend their props
    return Children.map(children, (child) => React.cloneElement(child, {
      variables
    }));
  }

  render () {
    return Children.only(this.getChildren());
  }
}

// Example
<Theme variables={{ 'primaryColor': 'red' }}>
  <Button />
  <SecondButton />
  <ThirdButton />
</Theme>

反应上下文

将变量传递到 React 树的任何部分的最简单方法是使用 React documentation 中描述的上下文(这也是确切的用例!):

// Context provider
class ThemeProvider extends Component {
  getChildContext() {
    return {
      theme: this.props.variables
    };
  }

  render() {
    return Children.only(this.props.children);
  }
}

ThemeProvider.childContextTypes = {
  theme: PropTypes.object.isRequired
};

// HOC
function Theme(WrappedComponent) {
  class ThemeWrapper extends Component {

    render() {
      return <WrappedComponent { ...this.props } />;
    }
  }

  ThemeWrapper.contextTypes = {
    theme: PropTypes.object.isRequired
  };

  return ThemeWrapper;
};

// Using the HOC
class Button extends Component {
  render () {
    return <button style={{ color: this.context.theme.primaryColor }} />;
  }
}

const ThemeButton = Theme(Button);

// Example
<ThemeProvider variables={{ 'primaryColor': 'red' }}>
  <div>
    <ThemeButton />
  </div>
</ThemeProvider>

Redux 商店

当您使用 Redux 时,您可以将每个需要主题的组件包装在 connect HOC 中,并将您的主题信息存储在 store 状态中。这是一种共享数据并避免上下文复杂性的简单方法:

class Button extends Component {
  render () {
    return <button style={{ color: this.props.theme.primaryColor }} />
  }
}

const ConnectedButton = connect((state) => ({ theme: state.theme }))(Button);

// Example
// During app setup
store.dispatch(setTheme({ 'primaryColor': 'red' }));

// Later
<div>
  <ConnectedButton />
</div>

希望这会有所帮助。

【讨论】:

    【解决方案2】:

    对于任何在钩子发布后来到这里的人,here is an excellent guide 了解如何通过上下文和钩子隐式传递道具。文章摘要:

    ThemeContext.js:

    const ThemeContext = React.createContext({});
    
    export const ThemeProvider = ThemeContext.Provider;
    export const ThemeConsumer = ThemeContext.Consumer;
    
    export default ThemeContext;
    

    主页.js:

    import {ThemeProvider} from './ThemeContext.js';
    
    const HomePage = () => {
      const handleClick = e => {};
      const currentTheme = {backgroundColor: 'black', color: 'white'};
      return (
        <ThemeProvider value={currentTheme}>
          <MyButton text='Click me!' onClick={handleClick} ... />
          ...
        </ThemeProvider>
      );
    }
    

    MyButton.js:

    import React, { useContext } from 'react';
    import ThemeContext from './ThemeContext';
    
    const MyButton = ({text, onClick}) => {
      const style = useContext(ThemeContext);
      return <button style={style} onClick={onClick}>{text}</button>;
    }
    

    【讨论】:

      【解决方案3】:

      如果您更喜欢创建高阶组件,可以参考http://gaearon.github.io/react-dnd/,但您仍然需要正确配置组件以确定是否应该应用高阶组件的 props。

      另外,为什么不创建一个包装器组件,例如 myButton

      export default class MyBytton(){
         render(){
           return <button { 'primaryColor': 'red' } />
         }
      }
      
      render(){
         return <MyButton otherProperty:"foo"/>
      }
      

      【讨论】:

      • 我不确定这将如何解决问题。正如问题中提到的,我已经通过children 实现了高阶组件和包装组件,但试图查看是否可以在不使用context 的情况下让全局道具传递下去
      猜你喜欢
      • 2019-06-07
      • 2020-12-17
      • 1970-01-01
      • 2022-01-25
      • 1970-01-01
      • 1970-01-01
      • 2019-02-26
      • 1970-01-01
      • 2019-10-15
      相关资源
      最近更新 更多