Каков надежный способ рендеринга динамического количества дочерних компонентов React с использованием шаблона компонента контейнера Redux?

Скажем, у меня есть функциональный компонент презентации React, например:

const Functional = (props) => {
  // do some stuff

  return (
    <div>
      // more HTML based on the props
    </div>
  );
}

Functional.propTypes = {
  prop1: React.PropTypes.string.isRequired,
  prop2: React.PropTypes.string.isRequired,
  // ...
};

Если я использую Redux и следую шаблону компонента контейнера, как лучше всего отобразить динамическое количество этих <Functional/> компонентов внутри компонента-оболочки на основе элементов внутри массива (который находится внутри моего состояния Redux)?

Например. Мое состояние Redux может выглядеть так:

{
  functionalItems: [
    {
      prop1: 'x1',
      prop2: 'y1',
      // ...
    },
    {
      prop1: 'x2',
      prop2: 'y2'
    },
    // ... more items
  ],
  // ...
}

Таким образом, каждый элемент в массиве functionalItems должен соответствовать компоненту <Functional/>, и все они отображаются рядом друг с другом.

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

Я опубликую решения, которые я могу придумать (но которые имеют нежелательные черты), как ответы на этот вопрос.


person pleasedesktop    schedule 09.01.2017    source источник
comment
Можете ли вы опубликовать пример ввода и вывода html, который вы ожидаете получить   -  person Luke101    schedule 09.01.2017
comment
@Luke101 Люк101, это вопрос о шаблоне проектирования, а не о том, что я пытаюсь заставить XYZ работать, но не могу, не могли бы вы мне помочь? типа вопрос.   -  person pleasedesktop    schedule 09.01.2017


Ответы (2)


Я хотел бы предложить вам передать весь массив компоненту-оболочке следующим образом:

const mapStateToProps = (state) => ({
    items: getFunctionalItems(state),
    // ...
});

а затем в своем Wrapper.jsx сделайте так:

const Wrapper = (props) => {

  const elements = props.items.map((item, index) => {
    <Functional prop1={ item.prop1 } prop2={ item.prop2 } ...
      key={ ... /* you can use index here */ }/>
  });

  return (
    <div>
      { elements }
    </div>
  );

};

...где getFunctionalItems() — это функция доступа, которая является каноническим средством доступа к функциональным элементам из состояния.

Таким образом, вы можете обрабатывать изменения в структуре состояния или другой макет рендеринга. (следовательно, более надежный (я думаю)). И это больше похоже на следование принципу единственной ответственности.

person JD Hernandez    schedule 09.01.2017
comment
Какая польза от прямой передачи массива, если вы даже не используете элементы напрямую, то есть вы все еще используете функции getPropValueX()? - person pleasedesktop; 09.01.2017
comment
Ничего страшного, я так и думал. Этот подход чище, но, на мой взгляд, он более хрупкий. Вы утверждаете, что это было бы устойчивым к изменениям в государственном устройстве, но я бы сказал обратное. Вы уповаете на госструктуру, в отличие от моего решения, которое этого не делает. - person pleasedesktop; 09.01.2017
comment
О да, я ошибаюсь, думая, что вы можете легко изменить код после изменения структуры состояния (это то, что я имел в виду, говоря, что вы можете обрабатывать изменения в структуре состояния). Итак, я предполагаю, что вы планируете осуществлять горячую замену компонентов контейнера с различными состояниями? - person JD Hernandez; 09.01.2017
comment
Не планируя горячую замену, как вы намекнули, я просто подумал, что лучше добавить уровень косвенности при доступе к данным из состояния/хранилища, чтобы при изменении структуры вы просто меняли метод доступа один раз, а не изменение всех прямых доступов в состояние/хранилище. т.е. Аналогия заключается в том, что в OO вам предлагается использовать методы или свойства аксессора/мутатора вместо того, чтобы напрямую работать с переменными класса (вне класса). Это происходит за счет некоторого шаблона, конечно. - person pleasedesktop; 09.01.2017
comment
Теперь я понимаю. Тогда ваш ответ должен быть самым близким, так как вам нужен дизайн мутатора/аксессора. В любом случае, вы можете использовать функцию карты, чтобы сделать код кратким. - person JD Hernandez; 09.01.2017
comment
Я немного отредактировал ваш ответ, чтобы получить лучшее из обоих миров. Можете ли вы увидеть его и, следовательно, оценить его? - person pleasedesktop; 09.01.2017
comment
Да. Я думаю, что это лучше. одобрил это - person JD Hernandez; 09.01.2017
comment
Спасибо. Пока я думаю, что это лучшее решение. Я оставлю его открытым на некоторое время, чтобы посмотреть, не появится ли лучший. Если нет, то я отмечу ваш ответ как принятый. - person pleasedesktop; 09.01.2017
comment
@pleasedesktop, добрый день, есть ли новости по этому поводу? Я просто хочу знать, было ли создано лучшее решение, поскольку я сейчас решаю эту проблему в своем проекте. - person JD Hernandez; 20.01.2017

Описание решения

  • Создайте компонент функциональной презентации-оболочки, который принимает количество и функции для получения значений опоры <Functional/>.
  • Создайте компонент-контейнер, который подключается к этому новому компоненту-оболочке и передает количество (на основе состояния Redux) и функции доступа для получения значений <Functional/> prop.

Пример кода

Обертка.jsx:

const Wrapper = (props) => {

  const elements = [];

  for (let i = 0; i < props.quantity; i++) {
    elements.push(
      <Functional prop1={ getPropValue1(i) } prop2={ getPropValue2(i) } ...
                  key={ ... }/>
    );
  }

  return (
    <div>
      { elements }
    </div>
  );
};

Wrapper.propTypes = {
  quantity: React.PropTypes.number.isRequired,
  getPropValue1: React.PropTypes.func.isRequired,
  getPropValue2: React.PropTypes.func.isRequired,
  // ...
};

КонтейнерКомпонент.js:

const mapStateToProps = (state) => ({
  quantity: state.functionalItems.length,
  getPropValue1: (index) => state.functionalItems[index].prop1,
  getPropValue2: (index) => state.functionalItems[index].prop2,
  // ...
});

const ContainerComponent = connect(mapStateToProps)(Wrapper);
person pleasedesktop    schedule 09.01.2017
comment
Это лучшее решение, которое я могу придумать, но оно все еще кажется мне хрупким/неуклюжим. Есть ли более элегантный подход? - person pleasedesktop; 09.01.2017