วิธีที่ถูกต้องในการแบ่งปันฟังก์ชันระหว่างส่วนประกอบใน React


92

ฉันมีส่วนประกอบหลายอย่างซึ่งต้องทำสิ่งเดียวกันทั้งหมด (ฟังก์ชั่นง่ายๆที่จับคู่ส่วนประกอบย่อยของพวกเขาและทำบางอย่างกับแต่ละองค์ประกอบ) ในขณะนี้ฉันกำลังกำหนดวิธีการนี้ในแต่ละส่วนประกอบ แต่ฉันต้องการกำหนดเพียงครั้งเดียว

ฉันสามารถกำหนดมันในองค์ประกอบระดับบนสุดแล้วส่งต่อลงไปเป็นเสา แต่นั่นรู้สึกไม่ถูกต้องนัก มันเป็นฟังก์ชั่นห้องสมุดมากกว่าเสา (มันเหมือนกับว่า).

อะไรคือวิธีที่ถูกต้องในการทำเช่นนี้?


ลองดูลิงค์นี้ หรือค้นหา "mixins" และ "HOC" ใน Google
Borzh

คำตอบ:


38

หากคุณใช้บางอย่างเช่นbrowserifyคุณสามารถมีไฟล์ภายนอกเช่น util.js ที่ส่งออกฟังก์ชันยูทิลิตี้บางอย่าง

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

exports.doSomething = doSomething;

จากนั้นจำเป็นต้องใช้

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

@AnkitSinghaniya ขึ้นอยู่กับว่าคุณใช้อะไรในการจัดการสถานะส่วนหน้าของแอปของคุณ?
deowk

1
ฉันใช้สถานะปฏิกิริยา
aks

ทำไมฉันถึงได้รับ 'doSomething' จึงไม่ได้กำหนดไว้ว่าไม่มีความคิดใด ๆ
Abhijit Chakra

48

Utils.js พร้อมไวยากรณ์ Javascript ES6 ล่าสุด

สร้าง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>

1
อะไรคือสิ่ง/fileที่ท้ายบรรทัดนำเข้าสำหรับ?
alex

@alex เป็นเพียงตัวอย่างเท่านั้น วางเส้นทางสัมพัทธ์ของคุณไปที่ util.js ที่นั่น
Fangming

14

หากคุณต้องการจัดการสถานะในฟังก์ชันตัวช่วยให้ทำตามสิ่งนี้:

  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()wrapper ส่วนประกอบนี้จะรับส่วนประกอบเป็นพารามิเตอร์และส่งคืนกลับมาพร้อมกับcomponentMapper()ฟังก์ชั่นที่ส่งผ่านลงมาเป็นเสา

นี่ถือเป็นแนวทางปฏิบัติที่ดีในการ React คุณสามารถดูรายละเอียดวิธีการทำได้ที่นี่


ฉันอยากรู้ว่าทำไม React ถึงกีดกันการสืบทอดองค์ประกอบที่รูปแบบ withComponent ดูเหมือนจะคล้ายกัน?
workwise

@ ด้วยการใช้การสืบทอดหมายถึงองค์ประกอบทั้งสอง
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 + (และฉันไม่เห็นประโยชน์ในกรณีนี้)


ข้อมูลเกี่ยวกับมิกซ์อินมีประโยชน์ ในกรณีนี้ฉันต้องการใช้ฟังก์ชันตามเงื่อนไขแทนที่จะให้บางสิ่งเกิดขึ้นโดยอัตโนมัติในเหตุการณ์วงจรชีวิต ดังนั้น. อย่างที่คุณบอกว่าฟังก์ชั่นยูทิลิตี้แบบเก่าล้วนเป็นหนทางที่จะไป

1
หมายเหตุ: React.createClassเลิกใช้งานตั้งแต่ React 15.5.0 create-react-app แนะนำให้ใช้โมดูล npm create-react-classแทน
Alex Johnson

ฉันต้องการทำสิ่งเดียวกับ OP แต่ฉันสับสนที่นี่เล็กน้อย คุณไม่แนะนำตัวเลือกใด ๆ ที่คุณระบุไว้ สิ่งที่ทำคุณแนะนำ?
kkuilla

ฉันจะสร้างไฟล์สำหรับฟังก์ชันเช่นdoSomething.jsหรือไฟล์ที่มีฟังก์ชัน "ยูทิลิตี้" ที่คล้ายกันหลาย ๆ ฟังก์ชันเช่นutils.jsและนำเข้าฟังก์ชันเหล่านั้นที่คุณต้องการใช้
Dominic

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

สไตล์ที่ 2 - ส่วนประกอบประเภท Utilสามารถสร้างได้เช่นนี้ใน./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 สำหรับสิ่งนี้หรือไม่? ดูhttps://facebook.github.io/react/docs/reusable-components.html

แม้ว่าพวกเขาจะไม่ได้รับความนิยมโปรดดูhttps://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750

อาจมีประโยชน์


ฉันยอมรับว่า mixin เป็นทางออกที่ดีกว่าการส่งออกฟังก์ชันทั่วไป
Tao Huang

@TaoHuang สิ่งที่ควรพิจารณาบางประการ 1. Mixins ไม่ใช่ข้อพิสูจน์ในอนาคตและจะถูกใช้น้อยลงในแอปสมัยใหม่ 2. การใช้ฟังก์ชันที่ส่งออกทำให้กรอบรหัสของคุณไม่เชื่อเรื่องพระเจ้า - คุณสามารถใช้ฟังก์ชันเหล่านี้ในโครงการ js อื่น ๆ ได้อย่างง่ายดาย โปรดอ่านโพสต์นี้เกี่ยวกับสาเหตุที่ไม่ใช้ Mixins -> facebook.github.io/react/blog/2016/07/13/…
deowk

มิกซินอาจเข้าถึงและแก้ไขสถานะในขณะที่ลักษณะไม่ได้และเนื่องจาก OP กำลังบอกว่าพวกเขาต้องการบางสิ่งที่จะถือว่าเป็นไลบรารีฟังก์ชันการมิกซ์อินจึงไม่ใช่ทางออกที่ถูก
HoldOffHunger

หากต้องการอัปเดตให้ใช้ฟังก์ชันลำดับที่สูงขึ้น facebook.github.io/react/docs/higher-order-components.html
Davet

ลิงค์แรกตาย
alex
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.