Tweedledum
@Tweedledum

Как грамотно использовать компзицию вместо наследования?

В проекте имеется здоровый класс компонента у которого есть компонент наследник. Моя задача избавиться от наследования и заменить его композицией.
То как композиция описана в документации реакта не дает мне почти ничего, но вот я нашел интересную статью, где описывается композуция при помощи функции connectToStores.

Мой вопрос, - в статье описано более менее стандартное решение, или эта функция должна быть в каждом отдельном случае своя? Может есть еще какие-нибудь толковые примеры/статьи реализации компзиции?
  • Вопрос задан
  • 426 просмотров
Пригласить эксперта
Ответы на вопрос 2
@kuftachev
Что-то Вы вообще не о том.

Смысл в том, что у нас есть общая логика, есть два варианта, вынести ее в базовый класс и наследоваться от него или сделать общий класс и сделать полем во всех остальных классах, где это нам нужно.

Недостаток первого варианта в том, что тяжело вносить изменения в будущем, особенно, если мы не контролирует все реализации нашего класса. Если мы хотим добавить метод или поле в базовый класс, то оно может конфликтовать с именем в классе наследнике и может все поломать. При композиции, естественно, такой проблемы нету.

Поэтому, рекомендуется использовать композицию, вместо наследования.

Ваш пример с React - это вообще не то. Даже тяжело представить как и зачем туда запихнуть наследование.
Ответ написан
Комментировать
miraage
@miraage
Старый прогер
Что, скорее всего, происходит. Компонент-наследник дёргает какой-то API компонента-родителя.
Вот это "переиспользуемую" часть выносите отдельно и имплементируете render props.

Супер вымышленный пример, но надеюсь посыл поняли.
// inheritance

class UrlLoader extends React.Component {
  state = {
    loading: false,
    result: null,
    error: null,
  };

  componentDidMount() {
    this.setState({
      loading: true,
    });

    this.load().then(
      (result) => {
        this.setState({
          result,
          loading: false,
        });
      },
      (error) => {
        this.setState({
          error,
          loading: false,
        });
      },
    );
  }

  load() {
    return fetch(this.props.url);
  }

  render() {
    const { loading, error, result } = this.state;

    if (loading) {
      return 'Loading...';
    }

    if (error) {
      return (
        <div className="error">
          {error}
        </div>
      );
    }

    return (
      <div className="page-result">
        {result}
      </div>
    );
  }
}

class FancyUrlLoader extends UrlLoader {
  componentDidMount() {
    super.componentDidMount();

    this.doSomethingElse();
  }

  doSomethingElse() {
    // something else
  }

  render() {
    const result = super.render();

    return (
      <div className="super-fancy">
        {result}
      </div>
    );
  }
}

// somewhere
const MyComponent = () => {
  return (
    <div>
      <FancyUrlLoader url="https://google.com" />
    </div>
  );
};


// composition

class UrlLoader extends React.Component {
  state = {
    loading: false,
    result: null,
    error: null,
  };

  componentDidMount() {
    this.setState({
      loading: true,
    });

    this.load().then(
      (result) => {
        this.setState({
          result,
          loading: false,
        });
      },
      (error) => {
        this.setState({
          error,
          loading: false,
        });
      },
    );
  }

  load() {
    return fetch(this.props.url);
  }

  render() {
    const { loading, error, result } = this.state;

    let render;

    if (loading) {
      render = 'Loading...';
    } else if (error) {
      render = (
        <div className="error">
          {error}
        </div>
      );
    } else {
      render = (
        <div className="page-result">
          {result}
        </div>
      );
    }

    return this.props.children(render);
  }
}

class FancyMaker extends React.Component {
  componentDidMount() {
    this.doSomethingElse();
  }

  doSomethingElse() {
    // something else
  }

  render() {
    return (
      <div className="super-fancy">
        {this.props.children}
      </div>
    );
  }
}

// somewhere
const MyComponent = () => {
  return (
    <div>
      <UrlLoader url="https://google.com">
        {(children) => (
          <FancyMaker>
            {children}
          </FancyMaker>
        )}
      </UrlLoader>
    </div>
  );
};
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы