Совместное использование контента между соседними компонентами, HOC?

У меня есть компонент-контейнер, внутри которого рендерится два компонента. В зависимости от некоторых свойств в контейнере верхний компонент может измениться, но нижний компонент всегда останется прежним.

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

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

Я новичок в React, поэтому мне сложно решить эту проблему с помощью реакции. Я кратко читал о рендеринге props / HOC, может быть, это то, что мне здесь нужно? Любая помощь будет принята с благодарностью.

Вот очень простой пример того, как это выглядит: https://codesandbox.io/s/zqo2p1yy9m


person Phil O'kelly    schedule 22.02.2019    source источник
comment
Вы можете поднять состояние и / или создать jsx и передать его как опору. Вам не нужны ни hocs, ни renderprops   -  person victor.ja    schedule 22.02.2019
comment
Да, как упоминалось в @ victor.ja, вам нужно поднять состояние вашего компонента. Статья, на которую он указал вам, - лучшее объяснение, которое вы можете получить: reactjs.org/docs/ Lifting-state-up.html Обратитесь к нам после прочтения, и если вы все еще не знаете, как решить эту проблему, мы можем помочь с примером.   -  person victmo    schedule 22.02.2019


Ответы (3)


Есть несколько способов решить эту проблему.

1) Вы можете использовать систему управления состоянием, такую ​​как Redux, mobx, или вы можете использовать контекстный API React https://reactjs.org/docs/context.html. ОДНАКО, поскольку вы новичок в React, я бы посоветовал не заниматься этим, пока вы не освоитесь с основным потоком.

2) Вы можете реализовать простой parent-child relationship для передачи данных между компонентами. В конце концов, это позволит вам обеспечить связь между соседними (родственными) компонентами. Как уже говорилось, этот поток обычно называют подъемом состояния. Собственно, по такому же принципу работает система государственного управления. Позвольте мне объяснить эту логику, также приняв во внимание ваш пример кода на основе сценария.

У нас будет компонент "Контейнер". Этот компонент будет компонентом с отслеживанием состояния. Единственная цель этого компонента-контейнера - иметь собственное целостное состояние, которое его потомки будут рассматривать как источник истины. Наличие пары методов для определения способов обновления этого целостного состояния. Дочерние компоненты будут чистыми или репрезентативными. Значит, у них не будет своих состояний. Они будут использоваться либо для отображения данных, которые передает им их родитель, либо для запуска методов, определенных их родителем, для обновления источника истинного состояния.

У нас будет два сценария: в сценарии 1 список содержимого будет представлен как есть. В сценарии 2 список содержимого будет представлен в обратном порядке букв.

class Container extends React.PureComponent {
  state = {
    reversed: false,
    newContent: "",
    contents: [
      {
        id: 1,
        text: "Initial Content"
      }
    ]
  };

  handleReverse = () => {
    this.setState((state) => ({
      ...state,
      reversed: !state.reversed
    }))
  }
  submitNewContent = () => {
    this.setState(state => ({
      ...state,
      contents: [
        ...state.contents,
        { id: Math.random(), text: state.newContent }
      ],
      newContent: ""
    }));
  };
  addNewContent = content => {
    this.setState({ newContent: content });
  };

  render() {
    return (
      <React.Fragment>
        <ContentCreator
        value={this.state.newContent}
        handleChange={this.addNewContent}
        handleClick={this.submitNewContent}
        />
        {this.state.reversed ? <ReversedContentDisplayer contents={this.state.contents} /> : <ContentDisplayer contents={this.state.contents} />}
        <Reversifier reversed={this.state.reversed} handleReverse={this.handleReverse}/>
      </React.Fragment>
    );
  }
}

Как видите, у контейнера есть только состояние, некоторые методы для обновления состояния и дочерний компонент. В его методе рендеринга не использовался ни один HTML-код.

Наш первый дочерний компонент - это ContentCreator

function ContentCreator({
  handleChange,
  handleClick,
  value
  }) {
  return (
    <div className="App">
      <label> New Content</label>
      <input type="text" value={value} onChange={event => handleChange(event.target.value)} />
      <button onClick={handleClick}>Add Content</button>
    </div>
  );
}

Этот компонент действует как форма (мне лень фактически обернуть его формой и функцией onSubmit). И основная цель этого компонента - обновить contents ключ нашего источника состояния истины.

Второй компонент называется Reversifier (хотя я не верю, что есть такое слово)

function Reversifier({reversed, handleReverse}) {
  return <button onClick={handleReverse}>{reversed ? 'Back to normal' : 'Reversify'}</button>
}

Основная цель этого компонента - переключить reversed ключ нашего источника состояния истины. Вы также можете видеть, что в зависимости от значения reversed текст внутри кнопки также изменяется (по-разному отражает различные сценарии)

И наши последние два компонента:

function ContentCreator({
  handleChange,
  handleClick,
  value
  }) {
  return (
    <div className="App">
      <label> New Content</label>
      <input type="text" value={value} onChange={event => handleChange(event.target.value)} />
      <button onClick={handleClick}>Add Content</button>
    </div>
  );
}

function ReversedContentDisplayer({ contents }) {
  return (
    <div className="App">
      {contents.map(content => (
        <div key={content.id}>{content.text.split("").reverse().join("")}</div>
      ))}
    </div>
  );
}

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

function ContentDisplayer({ contents, reversed }) {
  return (
    <div className="App">
      {contents.map(content => (
        <div key={content.id}>{reversed ? content.text.split("").reverse().join("") : content.text}</div>
      ))}
    </div>
  );
}

Но чтобы отразить разные сценарии, я создал два отдельных компонента.

Я создал для вас codeandbox на случай, если вы захотите поиграть с функциональностью, чтобы увидеть конечный результат https://codesandbox.io/s/04rowq150

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

Я надеюсь, что этот образец поможет вам в качестве отправной точки, и я надеюсь, что я правильно понимаю ваше требование как минимум на 51%. Если у вас есть дополнительные вопросы или затруднения, дайте мне знать

person Cagri Yardimci    schedule 22.02.2019

Природа реакции - сверху / снизу. Поэтому, если вам нужно поделиться частью состояния или некоторыми данными для передачи в качестве реквизита, вам нужно передать этот код его родительскому элементу. Передавать Jsx в качестве реквизита - это нормально, здесь вам нужно это сделать, и вы можете.

Дополнительная информация: состояние подъема

——————

А

/ \

B C

Итак, если у вас общий код между B и C, вы поднимаете его до A и передаете B и C в качестве реквизита. Если вам нужно состояние из B в C, вы поднимаете состояние до A. Если вам нужно получить доступ и изменить состояние из A в B или C, вы просто передаете функцию как обратный вызов в B или C.

person victor.ja    schedule 22.02.2019

Я согласен с victor.ja, но мне было интересно, есть ли у вас глобальный магазин, такой как redux. Затем вы можете просто вызвать действие для обновления состояния магазина из верхнего компонента. Компонент более высокого порядка может получать состояние хранилища как опору и обновлять компонент более низкого уровня.

person Anand Popat    schedule 22.02.2019