Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/27.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Reactjs 主干=>;React-高阶组件、继承和专业化_Reactjs_Inheritance_Backbone.js_Composition_Higher Order Components - Fatal编程技术网

Reactjs 主干=>;React-高阶组件、继承和专业化

Reactjs 主干=>;React-高阶组件、继承和专业化,reactjs,inheritance,backbone.js,composition,higher-order-components,Reactjs,Inheritance,Backbone.js,Composition,Higher Order Components,我有一个遗留的主干应用程序,我已经开始在React中重写。该应用程序有一个主视图,其中包含两个子视图,按纵向排列。顶部面板显示一些数据,底部面板显示将这些数据作为输入的某个算法的结果。因为我有许多不同的数据源,每个数据源都应用了不同的算法,所以我有一个抽象的基本视图类,然后为每个数据源创建子类,根据需要添加、修饰和重写方法。有点像这样: // Base View. const BaseView = Backbone.View.extend({ events: {}, initiali

我有一个遗留的主干应用程序,我已经开始在React中重写。该应用程序有一个主视图,其中包含两个子视图,按纵向排列。顶部面板显示一些数据,底部面板显示将这些数据作为输入的某个算法的结果。因为我有许多不同的数据源,每个数据源都应用了不同的算法,所以我有一个抽象的基本视图类,然后为每个数据源创建子类,根据需要添加、修饰和重写方法。有点像这样:

// Base View.
const BaseView = Backbone.View.extend({

  events: {},

  initialize() {
    this.subViewA = // instantiate subview...
    this.subViewB = // instantiate subview...
  },

  generateResultData() {
    // 'Abstract' method which should be specialised to generate data rendered by subViewB...
  },

  render() {
    // render subviews...
  },

});

// Derived View.
const Derived = BaseView.extend({

  events: {
    // event handlers...
  },

  add(a, b) {
    return a+b;
  },

  // additional methods...

  generateResultData() {
    return {
      result: this.add(2,2);
    }
  },

})
这将导致许多类似视图类的浅层次结构。这是非常必要的,但它是一个简单、直观、易于推理的模式,而且非常有效。然而,我很难看到如何在React中实现同样的效果。考虑到
React.Component
子类的子类化被认为是一种反模式,我的重点自然放在组合上,尤其是高阶组件。HOC(我觉得它很漂亮,但不直观,而且常常完全令人困惑)似乎涉及到添加通用功能,而不是专门化/细化更通用的功能。我还考虑过通过道具传递更专业的Componenet方法版本。但这只是意味着我必须反复使用相同的样板组件定义:

// General functional component, renders the result of prop function 'foo'.
function GeneralComponent(props) {
  const foo = this.props.foo || ()=>"foo";
  return (
    <div>
      <span> { this.props.foo() } </span>
    </div>
  )
}

// Specialised component 1, overrides 'foo'.
class MySpecialisedComponent extends React.Component {
  foo() {
    return this.bar()
  }

  bar() {
    return "bar"
  }

  render() {
    return (
      <GeneralComponent foo={this.foo} />
    )
  }
}

// Specialised component 2, overrides 'foo' and adds another method.
class MyOtherSpecialisedComponent extends React.Component {
  foo() {
    return this.bar() + this.bar()
  }

  bar() {
    return "bar"
  }

  baz() {
    return "baz"
  }

  render() {
    return (
      <GeneralComponent foo={this.foo} />
    )
  }
}
//通用函数组件,呈现prop函数“foo”的结果。
功能通用组件(道具){
const foo=this.props.foo | |(=>“foo”;
返回(
{this.props.foo()}
)
}
//专用组件1,覆盖“foo”。
类mySpecializedComponent扩展了React.Component{
foo(){
返回这个.bar()
}
bar(){
返回“bar”
}
render(){
返回(
)
}
}
//专用组件2覆盖“foo”并添加另一种方法。
类MyotherSpecializedComponent扩展了React.Component{
foo(){
返回this.bar()+this.bar()
}
bar(){
返回“bar”
}
baz(){
返回“baz”
}
render(){
返回(
)
}
}

显然,上面是一个非常简单的例子,但本质上抓住了我需要做的事情(尽管为了简单起见,我当然会操纵状态,但本例并没有这样做)。我是说,我可以这样做。但我想避免在所有地方重复这个样板。那么有没有一种更简单、更优雅的方法来实现这一点呢?

一般来说,如果组件是无状态的,并且不使用生命周期挂钩,那么它就没有理由成为
组件
类。在JavaScript中,充当名称空间而不持有状态的类可以被视为反模式

与其他一些框架相比,React没有需要映射变量才能在视图中使用的模板,因此需要提及
bar
函数的唯一地方就是调用它的地方。JSX是JavaScript的扩展,JSX表达式可以使用当前范围内可用的任何名称。这允许在不使用任何类的情况下组合函数:

const getBar => "bar";
const getBaz => "baz";
const getBarBaz => getBar() + getBaz();

const MySpecialisedComponent = props => <GeneralComponent foo={getBar} />;
const MyOtherSpecialisedComponent = props => <GeneralComponent foo={getBarBaz} />;

在我看来,让你失望的不是继承与组合,而是你的数据流:

例如,我的许多派生视图需要在主渲染之后进行自定义渲染。我使用的是第三方SVG库,呈现到“result”子视图中的数据来自于对其上方主数据视图中呈现的SVG元素的分析

所以你在这里要做的是在渲染后让一个远相关组件的子组件更新道具,对吗?像这样

// after the svg renders, parse it to get data
<div id="svg-container">
  <svg data="foo" />
  <svg data="bar />
</div>

// show parsed data from svg after you put it through your algos
<div id="result-container">
  // data...
</div>

在我看来,使用HOC抽象出在渲染调用中显式使用
.map
的劳动的模式(以及其他使用)就是您正在寻找的模式。然而,正如我上面所说的,HOC模式与跨兄弟组件共享数据存储的主要问题无关。

回答我自己的问题,这是我以前从未做过的

因此,我的问题实际上源于一种担忧,即我需要重构一个大型、命令式和有状态的代码库,以便与React的基于组合的模型(也包括Redux)集成。但在阅读了对我问题的(非常有见地和有帮助的)回答后,我突然想到我的应用程序有两个并行部分:UI和一个运行算法的引擎(实际上是一个音乐分析引擎)。我可以很容易地剥离引擎连接到的主干视图层。因此,使用React的API,我构建了一个“AnalysisEngineProvider”,它使引擎可用于子组件。引擎都是非常必要的,并且是典型的面向对象的,并且仍然使用主干模型,但是这对UI没有什么区别,因为后者不知道它的内部结构-这就是它应该是什么样子(模型可能在某个时候也会被重构)

引擎还负责呈现SVG(不带BB视图)。但React对此一无所知。它只看到一个空div。我从div中提取一个,并将其传递给引擎,以便引擎知道在哪里渲染。除此之外,引擎和UI几乎没有联系——DIV根本不会因React状态的更改而更新(不过,UI的其他组件显然是这样的)。引擎中的模型只会触发SVG的更新,而React对此一无所知


我对这种方法很满意,至少现在是这样——即使它只是一个增量重构的一部分,以实现一个完全反应的解决方案。无论我使用什么样的框架,它都是对应用程序的正确设计。

这些类的问题在于它们不能从类中获益,它们充当名称空间,可能是不合理的。一个更具体的例子将有助于更好地理解您的案例。Foos和bars解释了你在做什么,但没有解释如何改进。@estus确实如此。我为主干添加了一些更具体的内容
// after the svg renders, parse it to get data
<div id="svg-container">
  <svg data="foo" />
  <svg data="bar />
</div>

// show parsed data from svg after you put it through your algos
<div id="result-container">
  // data...
</div>
class AbstractDataMap extends PureComponent {
  static defaultProps = {
    data: [],
    map: (obj, i) => (<div key={i}>{obj}</div>)
  };

  render() {
    const { data, map, children } = this.props;
    const mapped = data.map(map);

    return (
      <Fragment>
        {mapped.map((obj, i) => (
          children(obj, i)
        ))}
      </Fragment>
    );
  }
}

// in some other container
class View extends Component {
  render() {
    return (
      <div>
        <AbstractDataMap data={[1, 2, 3]} map={(n) => ({ a: n, b: n + 1 })}>
          {({ a, b }, i) => (<div key={i}>a: {a}, b: {b}</div>)}
        </AbstractDataMap>

        <AbstractDataMap data={[2, 4, 6]} map={(n) => (Math.pow(n, 2))}>
          {(squared, i) => (<div key={i}>squared: {squared}</div>)}
        </AbstractDataMap>
      </div>
    );
  }
}