在 2 个 React DOM 之间传递数据

Pass data between 2 React DOM

在一个网站中,我有多个反应渲染元素。我想在这 2 individual 元素之间传递数据。在 2 个元素之间传递数据的可能选项是什么

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

我想在这些元素之间有一个单一的数据存储。就像我在一个组件中获取数据,我希望所有元素(在所有 ReactDOM 中)都可以访问该数据。 那么可能的选择是什么?

编辑:由于要求,我无法将它们合并到同一个根组件中。只有部分页面在响应中,其他部分仍在 HTML/Jquery 中。所以我在 dividually 中将它们呈现在所需的 div.

redux store 可以在不同的 ReactDOM 之间工作吗?

您可以创建一个组件容器,将两者都作为子容器,然后将它们分开呈现。通过道具和母容器交换数据。

我举个例子:

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")
);

容器组件

    class App extends React.Component{


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

}

使用 react-dom 库中的 Portals

Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.


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 中任意位置的容器中,或者创建一个新的 "dummy" 节点。在我的示例中,我假设有一个 ID 为 root 的空节点。然后将您的其他组件渲染到门户中。

Does redux store works between different ReactDOMs?

您的应用程序将像完全渲染到同一个容器中一样工作。如果您使用 Portals,您的组件将位于具有相同上下文的同一组件树中,同时在其他地方呈现。

我应该使用 Portal 吗?

使用 Portals 通常旨在用于视觉上需要 "break out" 其容器的组件,如模式或对话框。但是您也可以使用它们来创建可以在任何地方呈现的类似于小部件的组件。

创建通用门户组件

您还可以创建一个通用 <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 容器中。但他们仍然会共享相同的 React 组件树并拥有相同的商店提供者。

取决于两个组件之间的关系。

场景 I:如果他们只是兄弟姐妹,那么你可以将他们包装在你的容器组件中或者只是一个简单的 ,并将道具传递给 child,喜欢

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

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

然后在YourContainer中,你可以定义你的共享数据,并将它们添加到你的每个child like

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

场景二如果这两个组件相距很远 像上面一样,你想在 2 和 12 之间共享数据,那么你应该使用 redux 存储来共享数据,这是管理组件状态的中心位置,并通过 mapStateToProps,您可以找到更多详细信息 here

构建上下文系统:

  • 上下文是存储数据的对象。
  • 模块可以在上下文中访问和更新数据。
  • 上下文中的数据更新时可以通知模块。
  • 根据您的需要,模块可以自由地将数据添加到上下文中,也可以限制为仅更新现有数据。

这不是真正的反应特定问题。

上下文系统的示例如下所示:

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();