Cách đúng để chia sẻ chức năng giữa các thành phần trong React


90

Tôi có nhiều thành phần mà tất cả đều cần phải làm điều tương tự. (Một chức năng đơn giản ánh xạ qua các thành phần con của chúng và thực hiện điều gì đó với từng thành phần). Hiện tại, tôi đang xác định phương pháp này trong từng thành phần. Nhưng tôi chỉ muốn định nghĩa nó một lần.

Tôi có thể xác định nó trong thành phần cấp cao nhất và sau đó chuyển nó xuống như một chỗ dựa. Nhưng điều đó cảm thấy không hoàn toàn đúng. Nó là một chức năng thư viện hơn là một chỗ dựa. (Đối với tôi dường như).

Cách chính xác để làm điều này là gì?


Kiểm tra liên kết này . Hoặc tìm kiếm "mixin" và "HOC" trong google.
Borzh

Câu trả lời:


36

Nếu bạn sử dụng một cái gì đó như Browserify thì bạn có thể có một tệp bên ngoài, tức là use.js xuất một số chức năng tiện ích.

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

exports.doSomething = doSomething;

Sau đó, yêu cầu nó khi cần thiết

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

@AnkitSinghaniya Điều đó còn tùy thuộc, bạn đang sử dụng cái gì để quản lý trạng thái giao diện người dùng của ứng dụng?
deowk

1
Tôi đang sử dụng trạng thái phản ứng.
aks

Tại sao tôi nhận được 'doSomething' không được định nghĩa no-undef bất kỳ ý tưởng nào?
Abhijit Chakra

45

Utils.js với cú pháp Javascript ES6 mới nhất

Tạo Utils.jstệp như thế này với nhiều chức năng, v.v.

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

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

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

Sau đó, trong các thành phần mà bạn muốn sử dụng các chức năng sử dụng, hãy nhập các chức năng cụ thể cần thiết. Bạn không phải nhập mọi thứ

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

Và sau đó sử dụng các hàm này trong thành phần như sau:

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

Là gì /fileở phần cuối của dòng nhập khẩu đối với?
alex

@alex Đó chỉ là một ví dụ. Đặt đường dẫn tương đối của bạn đến use.js ở đó
Fangming

13

Nếu bạn muốn thao tác trạng thái trong các hàm trợ giúp, hãy làm theo điều này:

  1. Tạo tệp Helpers.js:

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

  2. Nhập hàm trợ giúp vào tệp thành phần của bạn:

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

  3. Trong hàm tạo của bạn, hãy thêm hàm trợ giúp đó vào lớp

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

  4. Trong chức năng kết xuất của bạn, hãy sử dụng nó:

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


10

Dưới đây là một số ví dụ về cách bạn có thể sử dụng lại một hàm ( FetchUtil.handleError) trong một thành phần React ( App).

Giải pháp 1: Sử dụng cú pháp mô-đun CommonJS

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

Giải pháp 2: Sử dụng "createClass" (React v16)

use / 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;

Lưu ý: Nếu bạn đang sử dụng React v15.4 (hoặc thấp hơn), bạn cần nhập createClassnhư sau:

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

Nguồn: https://reactjs.org/blog/2017/04/07/react-v15.5.0.html#migrating-from-reactcreateclass

Thành phần (sử dụng lại FetchUtil)

thành phần / 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

Một tùy chọn chắc chắn khác ngoài việc tạo tệp sử dụng sẽ là sử dụng thành phần có thứ tự cao hơn để tạo withComponentMapper()trình bao bọc. Thành phần này sẽ nhận một thành phần làm tham số và trả lại nó với componentMapper()hàm được truyền dưới dạng một chỗ dựa.

Đây được coi là một cách thực hành tốt trong React. Bạn có thể tìm hiểu cách thực hiện chi tiết tại đây.


Tôi tò mò là tại sao React không khuyến khích kế thừa thành phần trong đó mẫu withComponent có vẻ giống nhau?
workwise

@Wor Sử dụng kế thừa nghĩa là hai thành phần
Jake

4

Nghe có vẻ giống như một chức năng tiện ích, trong trường hợp đó, tại sao không đặt nó trong một mô-đun tiện ích tĩnh riêng?

Nếu không, nếu sử dụng một trình chuyển đổi như Babel, bạn có thể sử dụng các phương thức tĩnh của es7:

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

Hoặc nếu bạn đang sử dụng React.createClass, bạn có thể sử dụng đối tượng statics :

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

Tuy nhiên, tôi không khuyên các tùy chọn đó, không có ý nghĩa gì khi bao gồm một thành phần cho một phương thức tiện ích.

Ngoài ra, bạn không nên chuyển một phương thức xuống tất cả các thành phần của mình như một chỗ dựa, nó sẽ kết nối chặt chẽ chúng và khiến việc tái cấu trúc trở nên khó khăn hơn. Tôi khuyên một mô-đun tiện ích cũ đơn giản.

Tùy chọn khác là sử dụng mixin để mở rộng lớp, nhưng tôi không khuyên bạn nên điều đó vì bạn không thể làm điều đó trong es6 + (và tôi không thấy lợi ích trong trường hợp này).


Thông tin về mixin rất hữu ích. Trong trường hợp này, tôi muốn sử dụng hàm có điều kiện hơn là để điều gì đó xảy ra tự động trên một sự kiện vòng đời. Vì thế. Như bạn nói một chức năng tiện ích cũ đơn giản là cách để đi.

1
Lưu ý: React.createClasskhông được dùng nữa kể từ React 15.5.0. create-react-app đề xuất sử dụng mô-đun npm create-react-classthay thế.
Alex Johnson,

Tôi đang tìm cách làm điều tương tự như OP nhưng tôi hơi bối rối ở đây. Bạn không đề xuất bất kỳ tùy chọn nào mà bạn liệt kê. Có gì làm bạn khuyên?
kkuilla

Tôi chỉ đơn giản sẽ làm cho một tập tin cho các chức năng ví dụ doSomething.js, hoặc một tập tin với nhiều tương tự như "tiện ích" chức năng ví dụ utils.jsvà nhập các chức năng mà bạn cần để sử dụng chúng
Dominic

4

Tôi sẽ hiển thị hai kiểu bên dưới và bạn sẽ muốn chọn tùy thuộc vào mức độ logic của các thành phần liên quan đến nhau.

Kiểu 1 - Các thành phần tương đối liên quan có thể được tạo bằng các tham chiếu gọi lại, như thế này, trong ./components/App.js...

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

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

Và sau đó bạn có thể sử dụng các chức năng được chia sẻ giữa chúng như thế này ...

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

Kiểu 2 - Các thành phần kiểu Util có thể được tạo như thế này, trong ./utils/time.js...

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

Và sau đó chúng có thể được sử dụng như thế này, trong ./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"));
    }
}

Sử dụng cái nào?

Nếu logic tương đối liên quan (chúng chỉ được sử dụng cùng nhau trong cùng một ứng dụng), thì bạn nên chia sẻ trạng thái giữa các thành phần. Nhưng nếu logic của bạn có liên quan đến xa (tức là sử dụng toán học, sử dụng định dạng văn bản), thì bạn nên tạo và nhập các hàm lớp sử dụng.


2

Bạn không nên sử dụng Mixin cho việc này? Xem https://facebook.github.io/react/docs/reusable-components.html

Mặc dù chúng không được ưa chuộng, hãy xem https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750

Có thể hữu ích


Tôi đồng ý rằng mixin là một giải pháp tốt hơn ở đây thay vì chỉ xuất một hàm thông thường.
Tao Huang

@TaoHuang Một số điều cần xem xét, 1.Mixins không phải là bằng chứng trong tương lai và sẽ ngày càng được sử dụng ít hơn trong các ứng dụng hiện đại, 2.Sử dụng một chức năng đã xuất khiến khung mã của bạn trở nên bất khả tri - bạn có thể dễ dàng sử dụng chức năng đó trên bất kỳ dự án js nào khác. Ngoài ra, vui lòng đọc bài đăng này về lý do KHÔNG sử dụng Mixins -> facebook.github.io/react/blog/2016/07/13/…
deowk

Một mixin có thể truy cập và sửa đổi trạng thái, trong khi một đặc điểm thì không, và vì OP đang nói rằng họ muốn một thứ gì đó được coi là thư viện hàm, nên mixin sẽ không phải là giải pháp phù hợp.
HoldOffHunger

Để cập nhật điều này, hãy sử dụng các hàm thứ tự cao hơn. facebook.github.io/react/docs/higher-order-components.html
Davet

Liên kết đầu tiên đã chết
alex
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.