Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reuse a method from another component

Tags:

reactjs

I would like to pass the both states as the result of getSettings method in Settings component to Add component. Is there any way to do that without copying the method to the other component?

Thank you

Settings component

export class Settings extends Component {
    constructor(props) {
        super(props);

        this.state = {
         languages: [],
         currencies: []
        };
    }

    getSettings() {
        fetch('/settings', {
            method: 'get',
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
            }
        })
        .then( ... )
        .then(json => {
            this.setState({
                currencies: json.currencies,
                languages: json.languages
            });
        })
    }
}

Add component

export class Add extends Component {
    displayName = Add.name

constructor(props) {
    super(props);

    this.state = {
    languages: [],
    currencies: []
    };
}
like image 215
Damian Avatar asked Jul 27 '18 01:07

Damian


2 Answers

I can think of two ways where you can share your states in Settings.

  1. Using Child as Function/Render Prop pattern.
  2. Using the new React Context API.

Child as Function/Render Prop

you can simply pass the state into this.props.children or this.props.render.

class Settings extends Component {
  state = {
    languages: [],
    currencies: []
  };
  getSettings = () => {
    // fetch('/settings', {
    //     method: 'get',
    //     headers: {
    //         'Accept': 'application/json',
    //         'Content-Type': 'application/json'
    //     }
    // })
    // .then( ... )
    // .then(json => {
    //     this.setState({
    //         currencies: json.currencies,
    //         languages: json.languages
    //     });
    // })
    this.setState({
      currencies: ["Dollar", "Euro", "Won"],
      languages: ["English", "French", "Korean"]
    });
  };

  componentDidMount() {
    this.getSettings();
  }

  render() {
    return <div>{this.props.children(this.state)}</div>;
  }
}

And use it like this.

const renderComponents = (currencies, languages) => {
  const currencyItems = currencies.map(currency => (
    <li key={currency}>{currency}</li>
  ));
  const languageItems = languages.map(language => (
    <li key={language}>{language}</li>
  ));
  return (
    <div>
      <h3>currencies</h3>
      <ul>{currencyItems}</ul>
      <hr />
      <h3>languages</h3>
      <ul>{languageItems}</ul>
    </div>
  );
};

const AppChildAsFunction = () => (
  <div>
    <h2>Using Child as Function</h2>
    <Settings>
      {({ currencies, languages }) => renderComponents(currencies, languages)}
    </Settings>
  </div>
);

Using the new React Context API

Create and export new Settings provider, and consumer

SettingsContext.js

import React, { Component } from "react";

const defaultValue = {
  currencies: ["Dollar", "Euro", "Won"],
  languages: ["English", "French", "Korean"]
};
const { Provider, Consumer: SettingsConsumer } = React.createContext(
  defaultValue
);

class SettingsProvider extends Component {
  state = {
    languages: [],
    currencies: []
  };
  render() {
    return <Provider value={this.state}>{this.props.children}</Provider>;
  }
}

export { SettingsProvider, SettingsConsumer };

And the usage is about the same

App.js

import { SettingsProvider, SettingsConsumer } from "./SettingsContext";
...
const AppWithContext = () => (
  <div>
    <h2>Using Context API</h2>
    <SettingsConsumer>
      {({ currencies, languages }) => renderComponents(currencies, languages)}
    </SettingsConsumer>
  </div>
);

You can just pick and choose whichever one suits your taste.

Working Demo on CodeSandBox.
Edit so.answer.51549396

enter image description here

like image 199
dance2die Avatar answered Oct 23 '22 08:10

dance2die


High order component is good for this case. First you create method withSetting .

export const withSetting = (Component) => class extends Component {
  constructor(props) {
      super(props);
      this.state = {
       languages: [],
       currencies: []
      };
  }
  getSettings() {
      fetch('/settings', {
          method: 'get',
          headers: {
              'Accept': 'application/json',
              'Content-Type': 'application/json'
          }
      })
      .then( ...)
      .then(json => {
          this.setState({
              currencies: json.currencies,
              languages: json.languages
          });
      })
  }
  render() {
    return <Component getSetting={this.getSetting} {...this.state} />
  }
}

And in Add Component. You can call this.props.getSetting or use this.props. languages and this.props. currencies

import {withGetSetting} from './withGetSetting'

class Add extends React.Component {...}

export default withSetting(Add) 

You can read about HOC here https://reactjs.org/docs/higher-order-components.html

like image 28
Giang Le Avatar answered Oct 23 '22 08:10

Giang Le