Контекст реагирования: передача всех методов компонента одновременно - PullRequest
0 голосов
/ 04 октября 2018

Предположим, у меня есть контейнерный компонент для обработки логики приложения, который имеет много методов:

class ScreenContainer extends React.Component
{
    state = {
        inputs: { /* various properties for input values */ },
        thingyActive: false,
        someList: ["thing1", "thing2"],
        // ...etc.
    };

    handleInputChange = e => {
        const { name, value } = e.target;
        this.setState(prevState => ({
            inputs: { ...prevState.inputs, [name]: value }
        }));
    };

    toggleThingy = () => this.setState(prevState => ({
        thingyActive: !prevState.thingyActive
    }));

    coolMethod = () => { /* Do cool stuff */ };

    boringMethod = () => { /* Do boring stuff */ };

    // ...more methods...
}

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

const ScreenContext = React.createContext();

Чтобы передать методы либо додочерний компонент или значение поставщика контекста, кажется, что вам всегда приходится делать что-то вроде ниже (обратите внимание, что в этом примере я поднимаю «действия» в состояние в соответствии с рекомендациями, приведенными в документации React ).

class ScreenContainer extends React.Component
{
    constructor()
    {
        super();
        this.state = {
            // ...same state as before, plus:
            actions: {
                handleInputChange: this.handleInputChange,
                toggleThingy: this.toggleThingy,
                coolMethod: this.coolMethod,
                boringMethod: this.boringMethod,
                everySingleOtherMethod: this.everySingleOtherMethod,
                // ...on and on
            }
        };
    }

    // ...same methods as before...

    render()
    {
        return (
            <ScreenContext.Provider value={this.state}>
                {this.props.children}
            </ScreenContext.Provider>
        );
    }

Я искал способ избежать передачи их всех одного за другим.Возможное решение, которое я нашел, включает использование геттера и циклический просмотр свойств экземпляра класса следующим образом:

    get allMethods()
    {
        let output = {};

        for (var prop in this)
        {
            if (this.hasOwnProperty(prop) && typeof this[prop] === "function")
                output[prop] = this[prop];
        }

        return output;
    }

Тогда я могу просто сделать:

    // (in ScreenContainer constructor)

    this.state = {
        // ...state,
        actions: this.allMethods
    };

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

Кажется, что это достаточно просто и, кажется, работает просто отлично, пока это делается в конструкторе.Есть ли что-нибудь сумасшедшее по этому поводу?Это плохая практика или есть какие-то потенциальные побочные эффекты, о которых я не знаю?Может быть, есть лучший способ, по которому я скучаю?

РЕДАКТИРОВАТЬ

Я обновил пример, чтобы он был ближе к моему реальному коду;теперь он показывает, что могут делать методы, и использует настройку контекста вместо передачи методов в качестве подпорок к одному дочернему компоненту.

Ответы [ 2 ]

0 голосов
/ 04 октября 2018

Если класс не поддерживает состояние, и методы класса должны использоваться отдельно в качестве вспомогательных функций, они не должны быть частью класса, не говоря уже о компоненте класса.Класс действует как пространство имен в этом случае.В современном JavaScript модули используются как пространства имен.Это может быть:

export const coolMethod = () => { /* Do cool stuff */ };

export const coolerMethod = () => { /* Do even cooler stuff */ };

export const boringMethod = () => { /* Do boring but necessary stuff */ };

ScreenContainer компонент является примером «умного» компонента контейнера.Всегда предпочтительно перечислять переданные функции явно, а не передавать их все автоматически.ScreenContainer может получить частные методы в какой-то момент.И должна быть гарантия, что перехватчики жизненного цикла также не будут пропущены случайно.

Если предполагается, что у него один дочерний элемент, его можно применять как компонент более высокого порядка:

const withScreen(Comp) => {
  return class ScreenContainer extends React.Component {
    ...
    render() {
      return <Comp handleInputChange={this.handleInputChange} /* ... */ />;
    }
  }
}

В данном конкретном случае render можно отличить от переданных функций, поскольку последние являются методами экземпляра (функциями стрелок).Хотя этот вид магии обычно не рекомендуется, поскольку он может вызвать проблемы и не будет работать должным образом для частных методов, его можно сократить до:

    render() {
      const fns = {};

      for (const method of Object.keys(this)) {
        if (typeof this[method] === 'function')
          fns[method] = this[method];
      }

      return <Comp {...fns} {...this.props} />;
    }

Для нескольких детей: ScreenContainer childrenможет быть пройден, чтобы добавить реквизиты аналогичным образом.

Для косвенных дочерних элементов, контекстный API может использоваться для передачи функций.

Хотя есть возможность передать ScreenContainer this дочерним элементам,это не рекомендуется, потому что это нарушает инкапсуляцию и противоречит принципу наименьших привилегий .

0 голосов
/ 04 октября 2018

Один из способов сделать это - создать новый экземпляр в конструкторе дочернего компонента следующим образом:

class ChildComponent extends Component {
  constructor(props) {
  super(props);
  this.Container = new MyContainer();
}

Затем вы можете использовать любые методы, такие как:

this.Container.coolMethod()

Редактировать

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

...