Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I'd like to be able to set a global theme (set of variables) for all of my components to inherit from and extend their default variables. For example, I have a button component that has default styles (inline CSS) that refer to a set of variables such as primaryColor, ... and I'd like to be able to update these variables easily wherever I use these components without needing to explicitly pass them to the components.

For example, I'd like the following behavior where I could either (1) wrap them in a component and have primaryColor cascade down to each Button component or (2) export this component in a higher order component and feed update the props, etc... however, I cannot get ay of these methods to work. Perhaps, there is a better way or ...

(1)

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

(2)

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

export default Theme(SampleComponent)

This method works, as it's, obviously, passed down explicitly to each component:

render() {
  return (
    <div>
      <Button variables={{ 'primaryColor': 'red' }} />
      <SecondButton variables={{ 'primaryColor': 'red' }} />
      <ThirdButton variables={{ 'primaryColor': 'red' }} />
    </div>
  );
}
like image 476
Detuned Avatar asked Jun 21 '16 02:06

Detuned


People also ask

How do I pass a prop to a component?

To pass props, add them to the JSX, just like you would with HTML attributes. To read props, use the function Avatar({ person, size }) destructuring syntax. You can specify a default value like size = 100 , which is used for missing and undefined props.

Can I pass props to route component?

Here are few ways you can pass props to a route component. With the react-router v5, we can create routes by wrapping with a component, so that we can easily pass props to the desired component like this. Similarly, you can use the children prop in v5.

Can we pass props from child to parent component?

You can't pass props from child to parent in React, it's only one way (from parent to child). You should either: Put the state in the parent component and manipulate it from the child component by passing the setter function in the props.

Can I pass props to styled component?

Passed propsIf the styled target is a simple element (e.g. styled.div), styled-components passes through any known HTML attribute to the DOM. If it is a custom React component (e.g. styled(MyComponent)), styled-components passes through all props.


2 Answers

I can see a few ways that you might accomplish this:

Extending child props

Limited, but allows passing 'extra' props to direct children of a component:

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 context

The easiest way to pass variables to any part of the React tree is by using context as described in the React documentation (this exact use case too!):

// 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 store

As you are using Redux, you could wrap each component that needs to be themed in the connect HOC and store your theme information in the store state. This is a simple way to share data and avoids the complexities of context:

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>

Hope this helps.

like image 143
Ashley 'CptLemming' Wilson Avatar answered Oct 21 '22 07:10

Ashley 'CptLemming' Wilson


For anybody coming here post-hooks release, here is an excellent guide on how to implicitly pass props via context and hooks. A quick summary of the article:

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>;
}
like image 34
Benjamin U. Avatar answered Oct 21 '22 07:10

Benjamin U.