Cara yang benar untuk berbagi fungsi antar komponen di React

90

Saya memiliki banyak komponen yang semuanya perlu melakukan hal yang sama. (Fungsi sederhana yang memetakan komponen anak mereka dan melakukan sesuatu untuk masing-masing komponen). Saat ini saya sedang mendefinisikan metode ini di setiap komponen. Tapi saya hanya ingin mendefinisikannya sekali.

Saya bisa mendefinisikannya di komponen tingkat atas dan kemudian menyebarkannya sebagai penyangga. Tapi rasanya kurang tepat. Ini lebih merupakan fungsi perpustakaan daripada prop. (Menurut saya).

Apa cara yang benar untuk melakukan ini?


sumber
Lihat tautan ini . Atau cari "mixins" dan "HOC" di google.
Borzh

Jawaban:

36

Jika Anda menggunakan sesuatu seperti browserify maka Anda dapat memiliki file eksternal yaitu util.js yang mengekspor beberapa fungsi utilitas.

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

exports.doSomething = doSomething;

Kemudian membutuhkannya sesuai kebutuhan

var doSomething = require('./util.js').doSomething;
deowk
sumber
@AnkitSinghaniya Tergantung, apa yang Anda gunakan untuk mengelola status front end aplikasi Anda?
deowk
1
Saya menggunakan status reaksi.
aks
Mengapa saya mendapatkan 'doSomething' tidak didefinisikan no-undef ada ide?
Abhijit Chakra
45

Utils.js dengan sintaks Javascript ES6 terbaru

Buat Utils.jsfile seperti ini dengan banyak fungsi, dll

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

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

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

Kemudian di komponen Anda yang ingin Anda gunakan fungsi util, impor fungsi spesifik yang diperlukan. Anda tidak perlu mengimpor semuanya

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

Dan kemudian gunakan fungsi-fungsi ini di dalam komponen seperti ini:

justAnAlert();
<p>{doSomethingWithInput('hello')}</p>
Fangming
sumber
Untuk apa /filedi akhir baris impor?
alex
@alex Ini hanya sebuah contoh. Letakkan jalur relatif Anda ke util.js di sana
Fangming
13

Jika Anda ingin memanipulasi status dalam fungsi pembantu ikuti ini:

  1. Buat file Helpers.js:

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

  2. Impor fungsi pembantu di file komponen Anda:

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

  3. Dalam konstruktor Anda, tambahkan fungsi pembantu itu ke kelas

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

  4. Dalam fungsi render Anda, gunakan:

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

Jaskaran Singh
sumber
10

Berikut adalah beberapa contoh bagaimana Anda dapat menggunakan kembali fungsi ( FetchUtil.handleError) dalam komponen React ( App).

Solusi 1: Menggunakan sintaks modul CommonJS

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

Solusi 2: Menggunakan "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;

Catatan: Jika Anda menggunakan React v15.4 (atau lebih rendah), Anda perlu mengimpor createClasssebagai berikut:

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

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

Komponen (yang menggunakan kembali FetchUtil)

komponen / 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;
Benny Neugebauer
sumber
7

Opsi lain yang solid selain membuat file util adalah menggunakan komponen orde tinggi untuk membuat withComponentMapper()pembungkus. Komponen ini akan mengambil komponen sebagai parameter dan mengembalikannya dengancomponentMapper() fungsi yang diturunkan sebagai prop.

Ini dianggap sebagai praktik yang baik di React. Anda dapat mengetahui cara melakukannya secara detail di sini.

Jake
sumber
Saya penasaran mengapa React mencegah pewarisan komponen di mana pola withComponent tampak serupa?
workwise
@Wor Menggunakan warisan berarti dua komponen
Jake
4

Kedengarannya seperti fungsi utilitas, dalam hal ini mengapa tidak meletakkannya di modul utilitas statis terpisah?

Jika tidak, jika menggunakan transpiler seperti Babel Anda dapat menggunakan metode statis es7:

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

Atau jika Anda menggunakan React.createClass Anda dapat menggunakan objek statik :

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

Namun saya tidak menyarankan opsi tersebut, tidak masuk akal untuk menyertakan komponen untuk metode utilitas.

Anda juga tidak boleh melewatkan metode melalui semua komponen Anda sebagai penyangga, itu akan memasangkannya dengan erat dan membuat refactoring lebih menyakitkan. Saya menyarankan modul utilitas lama biasa.

Pilihan lainnya adalah menggunakan mixin untuk memperluas kelas, tetapi saya tidak menyarankan itu karena Anda tidak dapat melakukannya di es6 + (dan saya tidak melihat manfaatnya dalam kasus ini).

Dominic
sumber
Informasi tentang mixin berguna. Dalam hal ini saya ingin menggunakan fungsi secara bersyarat daripada membuat sesuatu terjadi secara otomatis pada acara siklus hidup. Begitu. Seperti yang Anda katakan, fungsi utilitas lama yang biasa adalah cara yang tepat.
1
Catatan: React.createClasstidak digunakan lagi sejak React 15.5.0. create-react-app menyarankan penggunaan modul npm create-react-classsebagai gantinya.
Alex Johnson
Saya ingin melakukan hal yang sama dengan OP tetapi saya agak bingung di sini. Anda tidak merekomendasikan opsi apa pun yang Anda daftarkan. Apa yang Anda rekomendasikan?
kkuilla
Saya hanya akan membuat file untuk fungsi misalnya doSomething.js, atau file dengan beberapa fungsi "utilitas" yang serupa misalnya utils.jsdan mengimpor fungsi-fungsi tersebut di mana Anda perlu menggunakannya
Dominic
4

Saya akan menunjukkan dua gaya di bawah ini, dan Anda akan ingin memilih tergantung pada seberapa banyak logika komponen berhubungan satu sama lain.

Gaya 1 - Komponen yang relatif terkait dapat dibuat dengan referensi callback, seperti ini, di ./components/App.js...

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

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

Dan kemudian Anda dapat menggunakan fungsi bersama di antara mereka seperti ini ...

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

Gaya 2 - Komponen tipe utilitas dapat dibuat seperti ini, di ./utils/time.js...

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

Dan kemudian mereka bisa digunakan seperti ini, di ./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"));
    }
}

Yang harus digunakan?

Jika logikanya relatif terkait (hanya digunakan bersama dalam aplikasi yang sama), Anda harus berbagi status antar komponen. Tetapi jika logika Anda berhubungan jauh (mis., Math util, text-formatting util), maka Anda harus membuat dan mengimpor fungsi kelas util.

HoldOffHunger
sumber
2

Bukankah Anda harus menggunakan Mixin untuk ini? Lihat https://facebook.github.io/react/docs/reusable-components.html

Meskipun mereka tidak disukai, lihat https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750

Semoga bermanfaat

Davet
sumber
Saya setuju bahwa mixin adalah solusi yang lebih baik di sini daripada hanya mengekspor fungsi umum.
Tao Huang
@TaoHuang Beberapa hal yang perlu dipertimbangkan, 1. Mixins bukanlah bukti masa depan dan akan semakin jarang digunakan di aplikasi modern, 2. Menggunakan fungsi yang diekspor membuat kerangka kode Anda agnostik - Anda dapat dengan mudah menggunakan fungsi tersebut di proyek js lainnya. Juga silakan baca posting ini tentang mengapa TIDAK menggunakan Mixins -> facebook.github.io/react/blog/2016/07/13/…
deowk
Sebuah mixin dapat mengakses dan mengubah status, sementara suatu sifat tidak, dan karena OP mengatakan bahwa mereka ingin diperlakukan sebagai pustaka fungsi, mixin bukanlah solusi yang tepat.
HoldOffHunger
Untuk memperbaruinya, gunakan fungsi urutan yang lebih tinggi. facebook.github.io/react/docs/higher-order-components.html
Davet
Link pertama sudah mati
alex