在React中,将共享状态注入到不是父子关系的组件中的正确方法是什么?

时间:2016-09-26 06:38:12

标签: reactjs

我遇到的问题是:我希望有多个按钮打开相同的模态,根据按下的按钮,模态将有不同的内容。

按钮放在一个组件中,模态放在另一个模块中(同样,它们不是父子关系)。

我有一个“状态对象”,用于保存有关单击了哪个按钮以及模式是打开还是关闭的信息。这个对象需要由BOTH组件读取和修改。

在Angular中,我将通过将“状态对象”注入两个组件来创建一个管理按钮状态和模态的服务。

我如何在React中解决这个问题?

(我在某地读到这可以通过使用Redux来完成,但目前我的应用程序没有实现通量架构,我需要一个不需要使用flux / redux的解决方案)

4 个答案:

答案 0 :(得分:3)

Redux的核心思想之一是拥有一个全局状态,并且只有DOM树中非常高的组件才会连接到它。你可以在没有Redux的React中模拟这个想法,将状态向上移动到组件树中,这个状态将基本上成为你的" Redux商店"。

  1. 找到要读取/修改对象的这些组件(最好是最低的组件)的公共祖先,并将对象保存为该组件中的state。对于不是根元素的每对组件,肯定会有一个共同的祖先(根)。
  2. 这个共同的祖先应该定义一个方法,将其作为props传递给这些组件,以便这些子组件可以调用操作并修改祖先中的状态。
  3. 下面显示了一些示例代码。如果您需要进一步澄清,请与我们联系。

    class Ancestor extends React.Component {
      constructor(props) {
        super(props);
        this.state = { object: null };
      }
    
      someMethod(data) {
        this.setState({
          object: data
        });
      }
    
      render() {
        return (
          <div>
            <div>
              <Button clickHandler={this.someMethod.bind(this)}/>
              <Button clickHandler={this.someMethod.bind(this)}/>
            </div>
            <Modal data={this.state.object} someProp={this.someMethod.bind(this)}/>
          </div>
        )
      }
    }
    
    class Button extends React.Component {
      doSomething() {
        this.props.clickHandler(someData);
      }
    
      render() {
        return (
          <button onClick={this.doSomething.bind(this)}/>Button</button>
        );
      }
    }
    

答案 1 :(得分:2)

通常你的情况是使用redux的完美案例。但是既然你需要一个非通量/ redux解决方案,你应该看一下如何使用React context Link to Context docs

  

有时,您希望在没有的情况下通过组件树传递数据   必须在每个级别手动传递道具。反应&#39; S   &#34;上下文&#34;功能可以让你这样做。

只有使用Context的要求是它需要一个包装器父组件。如果您没有,则需要添加一个。路线中的包装组件可以解决这个问题。

答案 2 :(得分:2)

如果您不能使用Redux,我看到的唯一解决方案是将模型传递给所有组件。您的数据应存储为单独的对象(模型),以实现用户可以进行的所有可能的更改。 e.g。

app.model = {
   addItem: function() { .. }
   update: function() { .. }    

}

然后,您可以将此对象作为道具传递给组件。

<MyButton model={app.model}>Add Item</MyButton>

var MyButton  = React.createClass({
  render: function() {
    return (
       <div className="coolButton" onClick={this.props.model.addItem}>
           {this.props.children}
       </div>
})

如果打开模态,则应保持模态或其父级的状态。

var MyModal  = React.createClass({

   getInitialState: function() {
     return {isShown: false};
   },

  render: function() {
    if (this.state.isShow===false) {
        return
    }
    return (
       <div className="modal">
           {this.props.children}
       </div>
})

答案 3 :(得分:1)

简短的回答 - 你不能在顶层组件中保持状态,这是一个限制,因为React只提供父子连接。

为解决此问题,已解决了许多不同的解决方案。人们使用像react-redux这样的容器来共享共同状态。他们的问题在于你仍然需要做一些工作并回答一些关于如何组成状态并连接它的问题。

这就是为什么我提出了一个更通用的解决方案,它允许你甚至从三个不同的分支“安装”组件。解决方案感觉就像文件系统中的符号链接。更详细的问题定义,src和示例可以在这里找到:https://github.com/fckt/react-layer-stack#rationale

  

原理

     

react / react-dom附带了两个基本假设/想法:

     
      
  • 每个UI都是自然分层的。这就是为什么我们有components相互包装的想法
  •   
  • react-dom默认情况下(物理上)将子组件安装到其父DOM节点
  •   
     

问题是有时第二个属性不是你想要的   在你的情况下。有时您想要将组件安装到   不同的物理DOM节点和之间保持逻辑连接   父母和孩子在同一时间。

     

典型的例子是类似Tooltip的组件:在某些时候   开发过程你可以发现你需要添加一些   您UI element的说明:它将在固定图层中呈现   应该知道它的坐标(UI element坐标或坐标   鼠标coords),同时它需要信息是否   需要立即显示或不显示,其内容和一些背景   父组件。此示例显示有时是逻辑层次结构   与物理DOM层次结构不匹配。

看一下https://github.com/fckt/react-layer-stack/blob/master/README.md#real-world-usage-example来看一下具体的例子(你可以在Layer中使用逻辑上的顶级状态):

import React, { Component } from 'react';
import { Layer, LayerContext } from 'react-layer-stack';
import FixedLayer from './demo/components/FixedLayer';

class Demo extends Component {
  render() {
    return (
      <div>
        <Layer use={ [this.state.counter] } id="lightbox2">{ (_, content) =>
          <FixedLayer style={ { marginRight: '15px', marginBottom: '15px' } }>
            { content } { this.state.counter }
          </FixedLayer>
        }</Layer>

        <LayerContext id="lightbox2">{({ showMe, hideMe }) => (
            <button onMouseLeave={ hideMe } onMouseMove={ ({ pageX, pageY }) => {
              showMe(
                <div style={{
                      left: pageX, top: pageY + 20, position: "absolute",
                      padding: '10px',
                      background: 'rgba(0,0,0,0.7)', color: '#fff', borderRadius: '5px',
                      boxShadow: '0px 0px 50px 0px rgba(0,0,0,0.60)'}}>
                   “There has to be message triage. If you say three things, you don’t say anything.”
                </div>)
            }}>Yet another button. Move your pointer to it.</button> )}
          </LayerContext>
      </div>
    )
  }
}