Передача данных между 2 React DOM

На веб-сайте у меня есть несколько элементов рендеринга реакции. Я хочу передать данные между этими двумя отдельными элементами. Какие возможны варианты передачи данных между 2 элементами

ReactDOM.render(<Header/>, document.getElementById('header'));
ReactDOM.render(<SideBar/>, document.getElementById('sidebar'));

Я хочу иметь единое хранилище данных между этими элементами. Например, я получаю данные в одном компоненте и хочу, чтобы эти данные были доступны во всех элементах (во всех ReactDOM). Итак, какие могут быть возможные варианты для этого?

Изменить: из-за требований я не могу объединить их в один и тот же корневой компонент. Только некоторая часть страницы находится в реакции, другая все еще находится в HTML/Jquery. Поэтому я визуализирую их по отдельности в нужном div.

Работает ли редукционное хранилище между разными ReactDOM?


person hardiksa    schedule 25.04.2018    source источник
comment
проверьте это также: javascriptstuff.com/component-communication   -  person Giorgi Moniava    schedule 25.04.2018


Ответы (5)


Используйте Порталы из библиотеки react-dom:

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


const HeaderPortal = ReactDOM.createPortal(<Header />, document.getElementById('header'))
const SideBarPortal = ReactDOM.createPortal(<SideBar />, document.getElementById('sidebar'))

const store = createStore(/* ... */)

ReactDOM.render(
  <YourDataStoreProvider store={store}>
    <HeaderPortal />
    <SideBarPortal />
  </YourDataStoreProvider>, 
  document.getElementById('root') // assuming you have an empty "dummy" node with that id
);

Просто визуализируйте свое приложение в контейнере в любом месте вашей модели DOM или создайте новый «фиктивный» узел. В моем примере я предполагаю, что есть пустой узел с идентификатором root. Затем визуализируйте другие компоненты в портал.

Работает ли редукционное хранилище между разными ReactDOM?

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

Стоит ли использовать порталы?

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

Создание универсального компонента портала

Вы также можете создать общий компонент <Portal>, который создает портал с контейнером id:

import {createPortal} from 'react-dom';

const Portal = ({children, container}) => createPortal(
    children,
    document.getElementById(container),
);

export default Portal;

И используйте это следующим образом:

ReactDOM.render(
  <YourDataStoreProvider store={store}>
    <Portal container="header">
      <Header />
    </Portal>
    <Portal container="sidebar">
      <SideBar />
    </Portal>
  </YourDataStoreProvider>, 
  document.getElementById('root')
);

ИЗМЕНИТЬ:

Вам нужен один узел в DOM, где вы можете отображать свое приложение. Это может быть новый элемент DOM, который вы создаете, или один из уже имеющихся у вас контейнеров. Если вы используете компонент <Portal> сверху, это также может выглядеть так:

ReactDOM.render(
    <YourDataStoreProvider store={store}>
        <Header /> // this will be visible in your header container
        <Portal container="sidebar">
            <SideBar /> // this will be rendered to the sidebar container
        </Portal>
    </YourDataStoreProvider>, 
    document.getElementById('header')
);

Это отобразит ваше приложение в контейнере header. Но только ваш компонент <Header> будет фактически иметь представление DOM в этом контейнере. Боковая панель будет отображаться порталом в контейнере sidebar. Но все же они будут иметь одно и то же дерево компонентов реакции и одного и того же поставщика хранилища.

person trixn    schedule 25.04.2018
comment
Из-за требований я не могу объединить их в один и тот же корневой компонент. Только некоторая часть страницы находится в реакции, другая все еще находится в HTML/Jquery. Поэтому я визуализирую их по отдельности в нужном div. Я проверю порталы в соответствии с вашим описанием, я смогу использовать его для разных DOM. - person hardiksa; 25.04.2018
comment
@hardiksa Тогда порталы - хороший способ решить эту проблему. Корневой элемент в моем примере — это не то, что уже должно быть там. Вы можете просто создать новый узел в теле и отобразить там свое приложение. На самом деле вы можете просто использовать контейнер заголовков в качестве корня для всего приложения и визуализировать все остальное с помощью порталов. Просто должен быть один узел, где вы фактически визуализируете приложение с помощью поставщика хранилища, потому что вы сказали, что хотите хранилище, доступное из всех ваших компонентов. - person trixn; 25.04.2018
comment
@hardiksa Я обновил свой ответ примером, который не требует третьего элемента DOM для рендеринга приложения. - person trixn; 25.04.2018
comment
Хорошо. поэтому я могу использовать один метод рендеринга для рендеринга каждого компонента в его отдельном div. Мне нужно отобразить только один реальный компонент, затем я могу добавить любое количество компонентов через портал, и все они будут использовать один и тот же провайдер, но отдельные элементы div в реальном html, верно? - person hardiksa; 25.04.2018
comment
@hardiksa Да, точно. Технически вам нужен один контейнер, который содержит ваше приложение с поставщиком магазина из-за того, как работает реакция. Это может быть один из контейнеров, которые у вас уже есть, или вы можете создать фиктивный контейнер с помощью document.createElement(). Если вы используете существующий, вы должны визуализировать все компоненты с порталами, которых не должно быть внутри этого контейнера. Ваше приложение будет работать так, как если бы все они отображались в одном контейнере. - person trixn; 25.04.2018

Вы можете создать контейнер компонентов и сделать их дочерними и визуализировать их отдельно. Обмен данными через реквизиты и материнский контейнер.

Я привожу пример:

https://codepen.io/hkares/pen/rvLPzQ

HTML

//html
<header id="header"></header>
<main>
  <nav id="nav"></nav>
  <section>I am section</section>
</main>
<footer>This is a footer</footer>
<div id="useless"></div>

Index.js

const HEADER_NODE = document.querySelector("#header");
const NAVBAR_NODE = document.querySelector("#nav");

const Header = ({ name }) => ReactDOM.createPortal(<h1>{name}</h1>, HEADER_NODE);

const NavBar = (props) => ReactDOM.createPortal(
  <article>
    <label>
      Set header name: <input {...props} />
    </label>
  </article>
, NAVBAR_NODE);

class Container extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: "this is a header"
    };
  }

  onChange = ({ target }) => this.setState({ value: target.value });

  render() {
    const { value } = this.state;
    return (
      <React.Fragment>
        <Header name={value} />
        <NavBar value={value} onChange={this.onChange} />
      </React.Fragment>
    );
  }
}

ReactDOM.render(
  <Container />,
  document.querySelector("#useless")
);
person Tomeu Cabot    schedule 25.04.2018
comment
Можете ли вы предоставить какой-либо код или ссылку, например, как я могу отображать их как отдельные. Мне нужно, чтобы методы рендеринга были независимыми, я не могу объединить их в один. Только некоторая часть страницы находится в реакции, другая все еще находится в HTML/Jquery. Поэтому я делаю их по отдельности. - person hardiksa; 25.04.2018

компонент контейнера

    class App extends React.Component{


    render(){
var data=[];
    return(<span>
     <Header data={data}/>
        <SideBar data={data}/>
        </span>);
    }

}
person EmCaye    schedule 25.04.2018
comment
Из-за требований я не могу объединить их в один и тот же компонент. Только некоторая часть страницы находится в реакции, другая все еще находится в HTML/Jquery. Поэтому я визуализирую их по отдельности в нужных разделах. - person hardiksa; 25.04.2018

Зависит от соотношения между двумя компонентами.

Сценарий I: если они просто братья и сестры, вы можете обернуть их в свой компонент-контейнер или просто в простой и передать реквизиты дочернему элементу, например

<div>
   <HeaderPortal data={yourSharedData}/>
   <SideBarPortal data={yourSharedData}/>
</div>

or

   <YourContainer>
       <HeaderPortal />
       <SideBarPortal/>
   </YourContainer>

Затем в YourContainer вы можете определить свои общие данные и добавить их к каждому из ваших дочерних элементов, например

render(){
   <div>
     {children.map(child=> React.cloneElement(child, {
         data: yourSharedData
      })}
   </div>
}

Сценарий II Если эти два компонента находятся далеко друг от друга введите описание изображения здесь Как и выше, вы хотите обмениваться данными между 2 и 12, тогда вам следует использовать redux хранилище для делитесь данными, которые являются центральным местом для управления состоянием вашего компонента, и делитесь любой его частью с требуемым компонентом через mapStateToProps, вы можете найти более подробную информацию здесь.

person Kevin Li    schedule 25.04.2018
comment
Из-за требований я не могу объединить их в один и тот же компонент. Только некоторая часть страницы находится в реакции, другая все еще находится в HTML/Jquery. Поэтому я визуализирую их по отдельности в нужном div. - person hardiksa; 25.04.2018

Создайте контекстную систему:

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

На самом деле это не проблема, связанная с реакцией.

Пример контекстной системы выглядит так:

content.js

import uniqueId from 'lodash/uniqueId';

let context = {
  foo: '',
  bar: ''
};

let listeners = {
  // key: [{ id, callback }]
};

let getAppContext = function(key) {
  return context[key];
};

let updateAppContext = function(key, value) {
  if (!context.hasOwnProperty(key)) {
    throw new Error(
      `Failed to update appContenxt: "${key}" does not exist.`
    );
  }

  let oldValue = context[key];
  context[key] = value;
  let callbacks = listeners[key];
  if (callbacks !== undefined && callbacks.length !== 0) {
    callbacks.forEach(function(item) {
      item.callback(value, oldValue);
    });
  }
};

let onAppContextUpdate = function(key, callback) {
  if (!context.hasOwnProperty(key)) {
    throw new Error(
      `Failed to add listener on appContenxt update: "${key}" does not exist.`
    );
  }

  if (listeners[key] === undefined) {
    listeners[key] = [];
  }

  let isListening = true;
  let id = uniqueId();
  listeners[key].push({ id, callback });

  return function removeListener() {
    if (isListening) {
      listeners[key] = listeners[key].filter(i => i.id !== id);
      isListening = false;
    }
  };
};

export {
  getAppContext,
  updateAppContext,
  onAppContextUpdate,
};

Использование

import { getAppContext, updateAppContext, onAppContextUpdate } from 'content.js';

// read foo
let foo = getAppContext('foo');

// update foo
updateAppContext('foo', 123);

// get notified when foo is updated
let removeListener = onAppContextUpdate('foo', function(newVal, oldVal) {
  console.log(newVal, oldVal);
});

// unlisten
removeListener();
person Chef    schedule 26.04.2018