Передача данных между 2 React DOM - PullRequest
0 голосов
/ 25 апреля 2018

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

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

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

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

Работает ли избыточный магазин между разными ReactDOM?

Ответы [ 5 ]

0 голосов
/ 26 апреля 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();
0 голосов
/ 25 апреля 2018

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

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

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

или

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

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

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

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

0 голосов
/ 25 апреля 2018

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

    class App extends React.Component{


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

}
0 голосов
/ 25 апреля 2018

Использовать Порталы из библиотеки 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?

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

Должен ли я использовать порталы?

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

Создание общего компонента портала

Вы также можете создать общий компонент <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')
);

EDIT

Вам нужен один узел в 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 порталом. Но они по-прежнему будут использовать одно и то же дерево реагирующих компонентов и иметь одного и того же поставщика магазина.

0 голосов
/ 25 апреля 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")
);
...