有没有办法隐式地将 props 传递给组件?

Is there a way to pass down props to a component implicitly?

我希望能够为我的所有组件设置一个全局主题(变量集)以继承和扩展它们的默认变量。例如,我有一个 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>
  );
}

如果你更喜欢创建高阶组件,你可以参考这个http://gaearon.github.io/react-dnd/但是你仍然需要正确配置你的组件来确定一些东西,比如高阶组件的道具是否应该应用.

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

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

render(){
   return <MyButton otherProperty:"foo"/>
}

我知道您可以通过几种方式完成此任务:

扩展子道具

有限制,但允许传递 'extra' 道具来引导组件的子组件:

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 中,并将你的主题信息存储在商店状态中。这是一种共享数据并避免上下文复杂性的简单方法:

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>

希望对您有所帮助。

对于来到这里的任何人 post-hooks 发布,here is an excellent guide 关于如何通过上下文和 hooks 隐式传递 props。文章的快速摘要:

ThemeContext.js:

const ThemeContext = React.createContext({});

export const ThemeProvider = ThemeContext.Provider;
export const ThemeConsumer = ThemeContext.Consumer;

export default ThemeContext;

HomePage.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>;
}