在React中组件之间共享功能的正确方法


90

我有多个组件都需要做同一件事。(一个简单的函数可以映射其子组件,并对每个子组件执行某些操作)。目前,我正在每个组件中定义此方法。但我只想定义一次。

我可以在顶级组件中定义它,然后将其作为道具传递。但这并不完全正确。它更多的是库函数而不是道具。(在我看来)。

正确的做法是什么?


查看此链接。或在Google中搜索“ mixins”和“ HOC”。
Borzh

Answers:


36

如果您使用类似browserify的工具,则可以拥有一个外部文件,即util.js,该文件可以导出一些实用程序功能。

var doSomething = function(num) {
 return num + 1;
}

exports.doSomething = doSomething;

然后根据需要

var doSomething = require('./util.js').doSomething;

@AnkitSinghaniya这取决于您使用什么来管理应用程序的前端状态?
deowk '16

1
我正在使用反应状态。
aks

为什么我没有定义'doSomething'的定义?
Abhijit Chakra'Apr

45

具有最新Javascript ES6语法的Utils.js

Utils.js像这样用多种功能创建文件

const someCommonValues = ['common', 'values'];

export const doSomethingWithInput = (theInput) => {
   //Do something with the input
   return theInput;
};

export const justAnAlert = () => {
   alert('hello');
};

然后,在要使用util函数的组件中,导入所需的特定函数。您不必导入所有内容

import {doSomethingWithInput, justAnAlert} from './path/to/utils.js/file'

然后在组件中使用这些功能,如下所示:

justAnAlert();
<p>{doSomethingWithInput('hello')}</p>

什么是/file在导入行的结束?
亚历克斯

@alex只是一个例子。将您的相对路径放到util.js

13

如果要在辅助函数中操作状态,请遵循以下步骤:

  1. 创建一个Helpers.js文件:

    export function myFunc(){ return this.state.name; //define it according to your needs }

  2. 在您的组件文件中导入助手功能:

    import {myFunc} from 'path-to/Helpers.js'

  3. 在构造函数中,将该辅助函数添加到类中

    constructor(){ this.myFunc = myFunc.bind(this) }

  4. 在渲染函数中使用它:

    render(){ <div>{this.myFunc()}</div> }


10

以下是一些有关如何FetchUtil.handleError在React组件(App)中重用函数()的示例。

解决方案1:使用CommonJS模块语法

module.exports = {
  handleError: function(response) {
    if (!response.ok) throw new Error(response.statusText);
    return response;
  },
};

解决方案2:使用“ createClass”(React v16)

util / FetchUtil.js

const createReactClass = require('create-react-class');

const FetchUtil = createReactClass({
  statics: {
    handleError: function(response) {
      if (!response.ok) throw new Error(response.statusText);
      return response;
    },
  },
  render() {
  },
});

export default FetchUtil;

注意:如果您使用的是React v15.4(或更低版本),则需要createClass按以下方式导入:

import React from 'react';
const FetchUtil = React.createClass({});

来源:https : //reactjs.org/blog/2017/04/07/react-v15.5.0.html#migrating-from-reactcreateclass

组件(重用FetchUtil)

组件/App.jsx

import Categories from './Categories.jsx';
import FetchUtil from '../utils/FetchUtil';
import Grid from 'material-ui/Grid';
import React from 'react';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {categories: []};
  }

  componentWillMount() {
    window
      .fetch('/rest/service/v1/categories')
      .then(FetchUtil.handleError)
      .then(response => response.json())
      .then(categories => this.setState({...this.state, categories}));
  }

  render() {
    return (
      <Grid container={true} spacing={16}>
        <Grid item={true} xs={12}>
          <Categories categories={this.state.categories} />
        </Grid>
      </Grid>
    );
  }
}

export default App;

7

除了创建util文件以外,另一个可靠的选择是使用更高阶的组件来创建withComponentMapper()包装器。该组件将一个组件作为参数,并componentMapper()通过传递为prop的函数将其返回。

在React中,这被认为是一种很好的做法。您可以在此处详细了解如何操作。


我很好奇为什么React在withComponent模式看起来相似的情况下不鼓励组件继承?
workwise

@Wor使用继承意味着两个部分
Jake

4

听起来像实用程序功能,在那种情况下为什么不将其放在单独的静态实用程序模块中呢?

否则,如果使用像Babel这样的编译器,则可以使用es7的静态方法:

class MyComponent extends React.Component {
  static someMethod() { ...

否则,如果您使用React.createClass可以使用静态对象:

var MyComponent = React.createClass({
  statics: {
    customMethod: function(foo) {
      return foo === 'bar';
    }
  }

但是,我不建议使用这些选项,为实用程序方法包括一个组件没有任何意义。

同样,您不应该在所有组件中传递方法作为道具,因为它将紧密地耦合它们并使重构更加痛苦。我建议使用一个普通的旧实用程序模块。

另一个选择是使用mixin扩展类,但是我不建议您这样做,因为您无法在es6 +中做到这一点(在这种情况下,我看不到任何好处)。


有关mixin的信息很有用。在这种情况下,我想有条件地使用该函数,而不是在生命周期事件中自动发生某些事情。所以。就像您说的那样,简单的旧实用程序功能是您的理想之选。

1
注意:React.createClass从React 15.5.0开始不推荐使用。create-react-app建议create-react-class改为使用npm模块。
亚历克斯·约翰逊

我想做和OP一样的事情,但是我有点困惑。您不建议您列出任何选项。你什么建议?
kkuilla

我只是为函数创建文件,例如doSomething.js,或具有多个类似“实用程序”函数的文件,utils.js然后将这些函数导入您需要使用的位置
Dominic'Bug

4

我将在下面显示两种样式,您将希望根据组件的逻辑相互关联的程度进行选择。

风格1 -相对相关部件可以创建与回调的引用,这样,在./components/App.js...

<SomeItem
    ref={(instance) => {this.childA = instance}}
/>

<SomeOtherItem
    ref={(instance) => {this.childB = instance}}
/>

然后您可以像这样使用它们之间的共享功能...

this.childA.investigateComponent(this.childB);  // call childA function with childB as arg
this.childB.makeNotesOnComponent(this.childA);  // call childB function with childA as arg

的Util型组件-类型2可以创建这样的,在./utils/time.js...

export const getTimeDifference = function (start, end) {
    // return difference between start and end
}

然后可以在...中像这样使用它们./components/App.js

import React from 'react';
import {getTimeDifference} from './utils/time.js';

export default class App extends React.Component {
    someFunction() {
        console.log(getTimeDifference("19:00:00", "20:00:00"));
    }
}

使用哪个?

如果逻辑是相对相关的(它们只能在同一应用程序中一起使用),则应在组件之间共享状态。但是,如果您的逻辑关系密切(例如,数学工具,文本格式工具),则应制作和导入工具类函数。


2

我同意,在这里,mixin是一个更好的解决方案,而不仅仅是导出一个通用函数。
陶黄

@TaoHuang需要考虑的一些事情:1.Mixins并不是面向未来的,并且将在现代应用程序中越来越少地使用。2.使用导出的函数使您的代码框架不可知-您可以在其他任何js项目上轻松使用这些函数。也请阅读这篇文章,为什么不使用混入- > facebook.github.io/react/blog/2016/07/13/...
deowk

混合可以访问和修改状态,而特性则不能,并且由于OP表示他们希望将某些东西视为功能库,所以混合不是正确的解决方案。
HoldOffHunger

要对此进行更新,请使用高阶函数。 facebook.github.io/react/docs/higher-order-components.html
Davet

第一链接死了
Alex
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.