มีบริการในแอปพลิเคชัน React


176

ฉันมาจากโลกเชิงมุมที่ฉันสามารถแยกตรรกะไปยังบริการ / โรงงานและกินมันในตัวควบคุมของฉัน

ฉันพยายามเข้าใจว่าฉันจะประสบความสำเร็จในแอปพลิเคชัน React ได้อย่างไร

สมมติว่าฉันมีองค์ประกอบที่ตรวจสอบการป้อนรหัสผ่านของผู้ใช้ (มันมีความแข็งแรง) มันเป็นตรรกะที่ค่อนข้างซับซ้อนดังนั้นฉันไม่ต้องการเขียนมันในองค์ประกอบที่เป็นของตัวเอง

ฉันควรเขียนตรรกะนี้ที่ไหน ในร้านค้าถ้าฉันใช้ฟลักซ์ หรือมีตัวเลือกที่ดีกว่า


คุณสามารถใช้แพคเกจและดูว่าพวกเขาทำมันอย่างไร - npmjs.com/package/react-password-strength-meter
James111

11
ความแข็งแรงของรหัสผ่านเป็นเพียงตัวอย่าง ฉันกำลังมองหาแนวปฏิบัติที่ดีที่สุดทั่วไป
Dennis Nerush

คุณอาจต้องทำข้างเซิร์ฟเวอร์?
James111

2
ไม่ได้เฉพาะตรรกะฝั่งไคลเอ็นต์ที่ไม่ควรอยู่ในองค์ประกอบโดยตรง ตัวตรวจสอบความแข็งแรงของรหัสผ่านเป็นเพียงตัวอย่าง
Dennis Nerush

4
หากคุณมีฟังก์ชั่นมากมายคุณสามารถเก็บไว้ในไฟล์ผู้ช่วยและเพียงแค่ต้องการมันไว้ในไฟล์คอมโพเนนต์ของคุณเพื่อใช้งาน ถ้ามันเป็นฟังก์ชั่นเดียวที่เกี่ยวข้องกับองค์ประกอบนั้นเพียงอย่างเดียวมันน่าจะอยู่ที่นั่นไม่ว่าจะซับซ้อน
Jesse Kernaghan

คำตอบ:


60

คำตอบแรกไม่ได้สะท้อนกระบวนทัศน์Container vs Presenterในปัจจุบัน

หากคุณต้องการทำบางสิ่งเช่นตรวจสอบรหัสผ่านคุณอาจมีฟังก์ชั่นที่ใช้งานได้ คุณจะส่งผ่านฟังก์ชันนั้นไปยังมุมมองที่นำมาใช้ซ้ำได้

ตู้คอนเทนเนอร์

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

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

ผู้ให้บริการ

หากจำเป็นต้องกำหนดค่าเพิ่มเติมอีกเล็กน้อยคุณสามารถใช้รูปแบบผู้ให้บริการ / ผู้บริโภค ผู้ให้บริการเป็นส่วนประกอบระดับสูงที่ห่อหุ้มบางส่วนใกล้กับและภายใต้วัตถุแอปพลิเคชันด้านบน (อันที่คุณติดตั้ง) และจัดหาส่วนหนึ่งของตัวเองหรือคุณสมบัติที่กำหนดค่าในเลเยอร์ด้านบนกับบริบท API จากนั้นฉันตั้งค่าองค์ประกอบคอนเทนเนอร์ของฉันเพื่อใช้บริบท

ความสัมพันธ์บริบทของพ่อแม่ / ลูกไม่จำเป็นต้องอยู่ใกล้กันเพียง แต่เด็กจะต้องสืบเชื้อสายมาไม่ทางใดก็ทางหนึ่ง Redux จัดเก็บและฟังก์ชัน React Router ในลักษณะนี้ ฉันใช้มันเพื่อจัดเตรียมบริบทการรูทสำหรับคอนเทนเนอร์ส่วนที่เหลือของฉัน (ถ้าฉันไม่ได้ระบุเอง)

(หมายเหตุ: บริบท API มีการทำเครื่องหมายการทดลองในเอกสาร แต่ฉันไม่คิดว่ามันจะเป็นอะไรอีกต่อไปพิจารณาว่ามีการใช้งานอะไร)

//An example of a Provider component, takes a preconfigured restful.js
//object and makes it available anywhere in the application
export default class RestfulProvider extends React.Component {
	constructor(props){
		super(props);

		if(!("restful" in props)){
			throw Error("Restful service must be provided");
		}
	}

	getChildContext(){
		return {
			api: this.props.restful
		};
	}

	render() {
		return this.props.children;
	}
}

RestfulProvider.childContextTypes = {
	api: React.PropTypes.object
};

มิดเดิ้ล

อีกวิธีหนึ่งที่ฉันไม่ได้ลอง แต่เห็นใช้แล้วคือการใช้มิดเดิลแวร์ร่วมกับ Redux คุณกำหนดวัตถุบริการของคุณนอกแอปพลิเคชันหรืออย่างน้อยก็สูงกว่าที่เก็บ redux ในระหว่างการสร้างร้านคุณฉีดบริการลงในมิดเดิลแวร์และมิดเดิลแวร์จัดการการกระทำใด ๆ ที่ส่งผลกระทบต่อบริการ

ด้วยวิธีนี้ฉันสามารถฉีดวัตถุ restful.js ของฉันลงในมิดเดิลแวร์และแทนที่วิธีการจัดเก็บด้วยการกระทำที่เป็นอิสระ ฉันยังต้องการส่วนประกอบคอนเทนเนอร์เพื่อให้การดำเนินการกับเลเยอร์มุมมองฟอร์ม แต่เชื่อมต่อ () และ mapDispatchToProps

v4 react-router-redux ใหม่ใช้วิธีนี้เพื่อส่งผลกระทบต่อสถานะของประวัติศาสตร์ตัวอย่างเช่น

//Example middleware from react-router-redux
//History is our service here and actions change it.

import { CALL_HISTORY_METHOD } from './actions'

/**
 * This middleware captures CALL_HISTORY_METHOD actions to redirect to the
 * provided history object. This will prevent these actions from reaching your
 * reducer or any middleware that comes after this one.
 */
export default function routerMiddleware(history) {
  return () => next => action => {
    if (action.type !== CALL_HISTORY_METHOD) {
      return next(action)
    }

    const { payload: { method, args } } = action
    history[method](...args)
  }
}


เพื่อนคำตอบที่ดีคุณหยุดฉันจากการทำสิ่งที่งี่เง่า 8) ความชื่นชม !!
csomakk

การใช้งานสำหรับตัวอย่างคอนเทนเนอร์คืออะไร?
อาจารย์

ฉันไม่สนับสนุน แต่ถ้าคุณต้องการลงเส้นทางบริการค้นหา (สิ่งที่คล้ายกับ Angular) คุณสามารถเพิ่มผู้ให้บริการ "injector / container" บางชนิดที่คุณแก้ไขบริการได้
eddiewould

ขอปฏิกิริยาตอบสนองต่อการช่วยเหลือ ด้วย Hooks คุณสามารถเขียนตรรกะที่นำกลับมาใช้ใหม่ได้โดยไม่ต้องเขียนชั้นเรียน reactjs.org/docs/…
Raja Malik

102

ปัญหากลายเป็นเรื่องง่ายมากเมื่อคุณทราบว่าบริการ Angular เป็นเพียงวัตถุที่ให้ชุดของวิธีการที่ไม่ขึ้นกับบริบท มันเป็นเพียงกลไก Angular DI ซึ่งทำให้ดูซับซ้อนมากขึ้น DI มีประโยชน์เนื่องจากจะดูแลการสร้างและดูแลอินสแตนซ์ให้กับคุณ แต่คุณไม่ต้องการมันจริงๆ

พิจารณาไลบรารี AJAX ยอดนิยมชื่อ axios (ซึ่งคุณอาจเคยได้ยิน):

import axios from "axios";
axios.post(...);

มันไม่ได้ทำงานเป็นบริการหรือไม่? มันมีชุดของวิธีการที่รับผิดชอบต่อตรรกะเฉพาะบางอย่างและเป็นอิสระจากรหัสหลัก

กรณีตัวอย่างของคุณเกี่ยวกับการสร้างชุดวิธีการแยกสำหรับตรวจสอบอินพุตของคุณ (เช่นการตรวจสอบความเข้มงวดของรหัสผ่าน) บางคนแนะนำให้ใส่วิธีการเหล่านี้ไว้ในส่วนประกอบซึ่งสำหรับฉันแล้วเห็นได้ชัดว่าเป็นการต่อต้านแบบ จะเกิดอะไรขึ้นถ้าการตรวจสอบเกี่ยวข้องกับการสร้างและประมวลผลการโทรแบ็คเอนด์ XHR หรือทำการคำนวณที่ซับซ้อน คุณจะผสมตรรกะนี้กับตัวจัดการการคลิกเมาส์และสิ่งอื่น ๆ ที่เฉพาะเจาะจงของ UI หรือไม่ เรื่องไร้สาระ เช่นเดียวกับวิธี container / HOC ห่อองค์ประกอบของคุณเพียงเพื่อเพิ่มวิธีการที่จะตรวจสอบว่าค่ามีตัวเลขในนั้นหรือไม่ มาเลย

ฉันเพิ่งจะสร้างไฟล์ใหม่ชื่อว่า 'ValidationService.js' และจัดระเบียบดังต่อไปนี้:

const ValidationService = {
    firstValidationMethod: function(value) {
        //inspect the value
    },

    secondValidationMethod: function(value) {
        //inspect the value
    }
};

export default ValidationService;

จากนั้นในองค์ประกอบของคุณ:

import ValidationService from "./services/ValidationService.js";

...

//inside the component
yourInputChangeHandler(event) {

    if(!ValidationService.firstValidationMethod(event.target.value) {
        //show a validation warning
        return false;
    }
    //proceed
}

ใช้บริการนี้จากทุกที่ที่คุณต้องการ หากกฎการตรวจสอบเปลี่ยนไปคุณจะต้องมุ่งเน้นไปที่ไฟล์ ValidationService.js เท่านั้น

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


3
นี่คือวิธีที่ฉันจะทำเช่นกัน ฉันค่อนข้างประหลาดใจที่คำตอบนี้มีคะแนนน้อยมากเพราะนี่เป็นวิธีที่มีแรงเสียดทานน้อยที่สุด หากบริการของคุณขึ้นอยู่กับบริการอื่น ๆ จากนั้นอีกครั้งก็จะเป็นการนำเข้าบริการอื่น ๆ ผ่านโมดูลของพวกเขา นอกจากนี้โมดูลโดยนิยาม singletons จึงมีเป็นจริงการทำงานต่อไปไม่จำเป็นในการ "ใช้มันเป็นโทนที่เรียบง่าย" - คุณได้รับพฤติกรรมที่ฟรี :)
มิกกี้ Puri

6
+1 - คำตอบที่ดีถ้าคุณใช้บริการที่มีฟังก์ชั่นเท่านั้น อย่างไรก็ตามบริการของแองกูลาร์เป็นคลาสที่กำหนดไว้เพียงครั้งเดียวจึงให้คุณสมบัติมากกว่าการส่งมอบฟังก์ชั่น คุณสามารถแคชวัตถุเป็นพารามิเตอร์คลาสบริการตัวอย่างเช่น
Nino Filiu

6
นี่ควรเป็นคำตอบที่แท้จริงไม่ใช่คำตอบที่ซับซ้อนเกินด้านบน
user1807334

1
นี่เป็นคำตอบที่ดียกเว้นว่าไม่ใช่ "ปฏิกิริยา" DOM จะไม่อัปเดตการเปลี่ยนแปลงตัวแปรภายในบริการ
Defacto

9
สิ่งที่เกี่ยวกับการฉีดขึ้นอยู่กับว่า? บริการเป็นไปไม่ได้ที่จะเยาะเย้ยในองค์ประกอบของคุณเว้นแต่คุณจะฉีดอย่างใด บางทีการมีออบเจ็กต์ระดับโลก "คอนเทนเนอร์" ระดับบนสุดที่มีแต่ละบริการเป็นฟิลด์จะได้รับสิ่งนี้ จากนั้นในการทดสอบของคุณคุณสามารถแทนที่เขตข้อมูลคอนเทนเนอร์ด้วย mocks สำหรับบริการที่คุณต้องการจำลอง
menehune23

34

ฉันต้องการตรรกะการจัดรูปแบบบางอย่างเพื่อใช้ร่วมกันในหลาย ๆ องค์ประกอบและในฐานะผู้พัฒนา Angular ก็เอนตัวไปสู่บริการอย่างเป็นธรรมชาติ

ฉันแบ่งปันตรรกะโดยใส่มันในไฟล์แยกกัน

function format(input) {
    //convert input to output
    return output;
}

module.exports = {
    format: format
};

จากนั้นนำเข้าเป็นโมดูล

import formatter from '../services/formatter.service';

//then in component

    render() {

        return formatter.format(this.props.data);
    }

8
นี่เป็นความคิดที่ดีตามที่ได้กล่าวถึงในเอกสาร React: reactjs.org/docs/composition-vs-inheritance.html หากคุณต้องการใช้ฟังก์ชันการทำงานที่ไม่ใช่ UI ระหว่างส่วนประกอบเราขอแนะนำให้แยกไฟล์ออกเป็นโมดูล JavaScript แยกต่างหาก ส่วนประกอบอาจนำเข้าและใช้ฟังก์ชันวัตถุหรือคลาสโดยไม่ต้องขยาย
user3426603

ที่จริงคำตอบเดียวที่นี่สมเหตุสมผล
Artem Novikov

33

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

คุณจะต้องใช้มันทุกครั้งที่ผู้ใช้จำเป็นต้องป้อนรหัสผ่านใหม่ นี่อาจเป็นบนหน้าจอการลงทะเบียนหน้าจอ "ลืมรหัสผ่าน" ผู้ดูแลระบบ "รีเซ็ตรหัสผ่านสำหรับหน้าจอผู้ใช้รายอื่น" เป็นต้น

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

สร้างส่วนประกอบ React ที่มีขนาดเล็กมากซึ่งประกอบด้วยฟิลด์อินพุตและตรรกะการตรวจสอบที่เกี่ยวข้องเท่านั้น ใส่องค์ประกอบนั้นในทุกรูปแบบที่อาจต้องการให้ใส่รหัสผ่าน

เป็นหลักผลลัพธ์เดียวกันกับการให้บริการ / โรงงานสำหรับตรรกะ แต่คุณกำลังเชื่อมต่อโดยตรงกับอินพุต ดังนั้นตอนนี้คุณไม่จำเป็นต้องบอกให้ฟังก์ชั่นนั้นว่าจะหาค่าอินพุตการตรวจสอบความถูกต้องของมันได้อย่างไร


11
สิ่งที่เป็นการปฏิบัติที่ไม่ถูกต้องในการจับคู่ตรรกะและ UI เพื่อที่จะเปลี่ยนลอจิกฉันจะต้องสัมผัสส่วนประกอบ
Dennis Nerush

14
ตอบโต้ความท้าทายพื้นฐานที่สมมติว่าคุณกำลังทำ มันแตกต่างอย่างสิ้นเชิงกับสถาปัตยกรรม MVC แบบดั้งเดิม วิดีโอนี้อธิบายได้ดีว่าทำไมจึงเป็นเช่นนั้น (ส่วนที่เกี่ยวข้องเริ่มประมาณ 2 นาที)
Jake Roby

8
จะทำอย่างไรถ้าต้องใช้ตรรกะการตรวจสอบความถูกต้องเดียวกันกับองค์ประกอบพื้นที่ข้อความ ตรรกะยังต้องถูกแยกเป็นไฟล์ที่แชร์ ฉันไม่คิดว่าออกนอกกรอบมีความเท่าเทียมกันจากห้องสมุดปฏิกิริยา บริการเชิงมุมเป็นแบบฉีดได้และกรอบเชิงมุมถูกสร้างขึ้นบนรูปแบบการออกแบบการฉีดขึ้นต่อกันซึ่งอนุญาตให้อินสแตนซ์ของการพึ่งพาที่จัดการโดยแองกูลาร์ เมื่อบริการถูกฉีดมักจะมีซิงเกิลตันในขอบเขตที่กำหนดเพื่อให้มีบริการเดียวกันใน React จำเป็นต้องมี DI บุคคลที่สามที่จะแนะนำแอปพลิเคชัน
Downhillski

15
@gravityplanx ฉันสนุกกับการใช้ React นี่ไม่ใช่รูปแบบเชิงมุมนี่เป็นรูปแบบการออกแบบซอฟต์แวร์ ฉันชอบที่จะเปิดใจของฉันในขณะที่ยืมสิ่งที่ฉันชอบจากส่วนที่ดีอื่น ๆ
Downhillski

1
@MickeyPuri ES6 โมดูลไม่เหมือนกับการพึ่งพาการฉีด
สป็อค

12

ฉันมาจากพื้นที่ Angular.js และบริการและโรงงานใน React.js นั้นง่ายกว่ามาก

คุณสามารถใช้ฟังก์ชั่นธรรมดาหรือคลาส, สไตล์การโทรกลับและเหตุการณ์ Mobx อย่างฉัน :)

// Here we have Service class > dont forget that in JS class is Function
class HttpService {
  constructor() {
    this.data = "Hello data from HttpService";
    this.getData = this.getData.bind(this);
  }

  getData() {
    return this.data;
  }
}


// Making Instance of class > it's object now
const http = new HttpService();


// Here is React Class extended By React
class ReactApp extends React.Component {
  state = {
    data: ""
  };

  componentDidMount() {
    const data = http.getData();

    this.setState({
      data: data
    });
  }

  render() {
    return <div>{this.state.data}</div>;
  }
}

ReactDOM.render(<ReactApp />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  
  <div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

</body>
</html>

นี่คือตัวอย่างง่ายๆ:


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

เพิ่งใช้งานสิ่งนี้ วิธีที่คุณทำให้มันเป็นคลาสและส่งออกมันสวยหรู
GavinBelson

10

สถานการณ์เดียวกัน: การทำโครงการเชิงมุมหลายโครงการและย้ายไปทำปฏิกิริยาไม่มีวิธีง่ายๆในการให้บริการผ่าน DI ดูเหมือนว่าเป็นชิ้นส่วนที่ขาดหายไป

ด้วยการใช้บริบทและเครื่องมือตกแต่ง ES7 เราสามารถเข้าใกล้:

https://jaysoo.ca/2015/06/09/react-contexts-and-dependency-injection/

ดูเหมือนว่าคนเหล่านี้ได้ก้าวไปอีกขั้น / ไปในทิศทางที่แตกต่าง:

http://blog.wolksoftware.com/dependency-injection-in-react-powered-inversifyjs

ยังรู้สึกเหมือนทำงานกับเมล็ดข้าว จะทบทวนคำตอบนี้อีกครั้งในเวลา 6 เดือนหลังจากดำเนินโครงการ React สำคัญ

แก้ไข: ย้อนกลับ 6 เดือนต่อมาด้วยประสบการณ์การตอบโต้ที่มากขึ้น พิจารณาลักษณะของตรรกะ:

  1. มีการผูก (เฉพาะ) กับ UI หรือไม่ ย้ายไปไว้ในองค์ประกอบ (คำตอบที่ยอมรับได้)
  2. มันเชื่อมโยงกับการจัดการของรัฐเท่านั้น ย้ายไปเป็นthunk
  3. ผูกไว้กับทั้งสอง? ย้ายไปที่ไฟล์แยกกันกินเป็นส่วนประกอบผ่านตัวเลือกและใน thunks

บางคนก็เข้าถึงHOCsเพื่อนำมาใช้ใหม่ แต่สำหรับฉันข้างต้นครอบคลุมเกือบทุกกรณีการใช้งาน นอกจากนี้ให้พิจารณาปรับสัดส่วนการจัดการสถานะโดยใช้เป็ดเพื่อแยกข้อกังวลออกและระบุ UI เป็นศูนย์กลาง


imho ผมคิดว่ามีเป็นวิธีง่ายๆในการให้บริการผ่าน DI, โดยใช้ระบบ ES6 โมดูล
มิกกี้ Puri

1
@MickeyPuri, โมดูล ES6 DI จะไม่รวมถึงลักษณะเชิงลำดับชั้นของ Angular DI เช่น parent (ใน DOM) อินสแตนซ์และการแทนที่เซอร์วิสที่จัดให้กับคอมโพเนนต์ชาย Imho ES6 module DI เปรียบเทียบกับระบบ DI ส่วนแบ็คเอนด์เช่น Ninject และ Structuremap โดยแยกออกจากลำดับชั้นขององค์ประกอบ DOM มากกว่า แต่ฉันต้องการที่จะได้ยินความคิดของคุณเกี่ยวกับมัน
โค

6

ฉันมาจาก Angular เช่นกันและลองทำ React ณ ตอนนี้มีวิธีหนึ่งที่แนะนำ (?) ดูเหมือนว่าจะใช้ส่วนประกอบที่สั่งซื้อสูง :

ส่วนประกอบลำดับสูงกว่า (HOC) เป็นเทคนิคขั้นสูงใน React สำหรับการนำองค์ประกอบเชิงตรรกะกลับมาใช้ใหม่ HOCs ไม่ได้เป็นส่วนหนึ่งของ React API พวกเขาเป็นรูปแบบที่เกิดขึ้นจากธรรมชาติขององค์ประกอบของปฏิกิริยา

สมมติว่าคุณมีinputและtextareaและชอบที่จะใช้ตรวจสอบตรรกะเดียวกัน

const Input = (props) => (
  <input type="text"
    style={props.style}
    onChange={props.onChange} />
)
const TextArea = (props) => (
  <textarea rows="3"
    style={props.style}
    onChange={props.onChange} >
  </textarea>
)

จากนั้นเขียน HOC ที่ตรวจสอบและห่อองค์ประกอบ

function withValidator(WrappedComponent) {
  return class extends React.Component {
    constructor(props) {
      super(props)

      this.validateAndStyle = this.validateAndStyle.bind(this)
      this.state = {
        style: {}
      }
    }

    validateAndStyle(e) {
      const value = e.target.value
      const valid = value && value.length > 3 // shared logic here
      const style = valid ? {} : { border: '2px solid red' }
      console.log(value, valid)
      this.setState({
        style: style
      })
    }

    render() {
      return <WrappedComponent
        onChange={this.validateAndStyle}
        style={this.state.style}
        {...this.props} />
    }
  }
}

ตอนนี้ HOC เหล่านั้นมีพฤติกรรมการตรวจสอบที่เหมือนกัน:

const InputWithValidator = withValidator(Input)
const TextAreaWithValidator = withValidator(TextArea)

render((
  <div>
    <InputWithValidator />
    <TextAreaWithValidator />
  </div>
), document.getElementById('root'));

ฉันสร้างง่ายสาธิต

แก้ไข : อีกตัวอย่างหนึ่งคือการใช้อุปกรณ์ประกอบฉากเพื่อส่งผ่านฟังก์ชั่นต่างๆเพื่อให้คุณสามารถแบ่งปันตรรกะที่ประกอบด้วยการตรวจสอบความถูกต้องของฟังก์ชั่นหลาย ๆHOCอย่างเช่น:

<InputWithValidator validators={[validator1,validator2]} />
<TextAreaWithValidator validators={[validator1,validator2]} />

Edit2 : React 16.8+ มอบฟีเจอร์ใหม่Hookซึ่งเป็นอีกวิธีที่ดีในการแบ่งปันตรรกะ

const Input = (props) => {
  const inputValidation = useInputValidation()

  return (
    <input type="text"
    {...inputValidation} />
  )
}

function useInputValidation() {
  const [value, setValue] = useState('')
  const [style, setStyle] = useState({})

  function handleChange(e) {
    const value = e.target.value
    setValue(value)
    const valid = value && value.length > 3 // shared logic here
    const style = valid ? {} : { border: '2px solid red' }
    console.log(value, valid)
    setStyle(style)
  }

  return {
    value,
    style,
    onChange: handleChange
  }
}

https://stackblitz.com/edit/react-shared-validation-logic-using-hook?file=index.js


ขอบคุณ. ฉันเรียนรู้จากวิธีนี้จริงๆ จะทำอย่างไรถ้าฉันต้องการตัวตรวจสอบมากกว่าหนึ่งตัว ตัวอย่างเช่นนอกเหนือจากตัวตรวจสอบความถูกต้องตัวอักษร 3 ตัวจะเกิดอะไรขึ้นถ้าฉันต้องการให้มีตัวตรวจสอบความถูกต้องอีกตัวหนึ่งที่ทำให้แน่ใจว่าไม่มีการป้อนตัวเลข เราสามารถเขียนผู้ตรวจสอบความถูกต้องได้หรือไม่?
Youssef Sherif

1
@YoussefSherif คุณสามารถเตรียมฟังก์ชั่นการตรวจสอบความถูกต้องหลายอย่างและส่งต่อให้เป็นอุปกรณ์ประกอบฉากHOCดูการแก้ไขของฉันสำหรับการสาธิตอื่น
บ๊อบ

ดังนั้น HOC จึงเป็นส่วนประกอบของคอนเทนเนอร์
อาจารย์

ใช่จาก React doc: "โปรดทราบว่า HOC ไม่ได้แก้ไของค์ประกอบอินพุตและไม่ใช้การสืบทอดเพื่อคัดลอกพฤติกรรมของมัน แต่ HOC จะรวบรวมส่วนประกอบดั้งเดิมโดยการห่อไว้ในส่วนประกอบคอนเทนเนอร์ HOC เป็นของแท้ ฟังก์ชั่นที่มีผลข้างเคียงเป็นศูนย์ "
บ๊อบ

1
ความต้องการคือการฉีดลอจิกฉันไม่เห็นว่าทำไมเราต้องใช้ HOC เพื่อทำสิ่งนี้ ในขณะที่คุณสามารถทำได้ด้วย HOC มันให้ความรู้สึกที่ซับซ้อนกว่า ความเข้าใจของฉันเกี่ยวกับ HOCs คือเมื่อมีสถานะเพิ่มเติมบางอย่างที่ต้องเพิ่มและจัดการเช่นไม่ใช่ตรรกะที่บริสุทธิ์ (ซึ่งเป็นกรณีที่นี่)
Mickey Puri

4

การบริการไม่ได้ จำกัด อยู่ที่ Angular แม้แต่ในAngular2 + ,

บริการเป็นเพียงชุดของฟังก์ชั่นตัวช่วย ...

และมีหลายวิธีในการสร้างและนำมาใช้ซ้ำในแอปพลิเคชัน ...

1)พวกมันสามารถแยกฟังก์ชั่นทั้งหมดซึ่งจะถูกส่งออกจากไฟล์ js คล้ายกับด้านล่าง

export const firstFunction = () => {
   return "firstFunction";
}

export const secondFunction = () => {
   return "secondFunction";
}
//etc

2)นอกจากนี้เรายังสามารถใช้วิธีการโรงงานเช่นกับคอลเลกชันของฟังก์ชั่น ... ด้วยES6มันสามารถเป็นชั้นเรียนมากกว่าฟังก์ชั่นคอนสตรัค:

class myService {

  constructor() {
    this._data = null;
  }

  setMyService(data) {
    this._data = data;
  }

  getMyService() {
    return this._data;
  }

}

ในกรณีนี้คุณต้องสร้างอินสแตนซ์ด้วยรหัสใหม่ ...

const myServiceInstance = new myService();

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

3)ถ้าฟังก์ชั่นและอุปกรณ์ของคุณไม่ถูกแชร์คุณสามารถใส่มันลงไปใน React component ในกรณีนี้เช่นเดียวกับฟังก์ชั่นในส่วนที่ตอบสนองของคุณ ...

class Greeting extends React.Component {
  getName() {
    return "Alireza Dezfoolian";
  }

  render() {
    return <h1>Hello, {this.getName()}</h1>;
  }
}

4)อีกวิธีหนึ่งที่คุณอาจจัดการกับสิ่งต่าง ๆ อาจใช้Reduxเป็นที่เก็บชั่วคราวสำหรับคุณดังนั้นถ้าคุณมีมันในแอปพลิเคชัน Reactมันสามารถช่วยคุณได้ด้วยฟังก์ชั่น setter getterมากมายที่คุณใช้ ... มันเหมือนร้านใหญ่ ที่คอยติดตามสถานะของคุณและสามารถแชร์ข้ามองค์ประกอบของคุณได้ดังนั้นสามารถกำจัดความเจ็บปวดมากมายสำหรับสิ่งต่างๆที่เราใช้ในบริการ ...

เป็นการดีที่จะทำรหัส DRYและไม่ต้องทำซ้ำสิ่งที่จำเป็นในการทำให้รหัสนั้นสามารถนำมาใช้ซ้ำได้และอ่านได้ แต่อย่าพยายามทำตามวิธี Angular ในแอป Reactดังที่กล่าวไว้ในข้อ 4 การใช้ Redux สามารถลดความต้องการ บริการและคุณ จำกัด การใช้งานสำหรับฟังก์ชั่นตัวช่วยที่สามารถใช้ซ้ำได้เช่นรายการ 1 ...


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

"อย่าทำตามวิธีเชิงมุมในการตอบสนอง" .. ahem Angular ส่งเสริมการใช้ Redux และสตรีมร้านค้าไปยังองค์ประกอบการนำเสนอโดยใช้ Observables และการจัดการของรัฐที่คล้าย Redux เช่น RxJS / Store .. คุณหมายถึง AngularJS หรือเปล่า เพราะว่ามันเป็นอีกเรื่องหนึ่ง
สป็อค

1

ฉันอยู่ในการบูตเช่นเดียวกับคุณ ในกรณีที่คุณพูดถึงฉันจะใช้ส่วนประกอบ UI การตรวจสอบความถูกต้องของอินพุตเป็นองค์ประกอบการตอบสนอง

ฉันยอมรับการใช้งานตรรกะการตรวจสอบตัวเองไม่ควรจะต้องควบคู่กัน ดังนั้นฉันจะใส่ลงในโมดูล JS แยก

นั่นคือสำหรับตรรกะที่ไม่ควรใช้ควบคู่กับโมดูล JS / คลาสในไฟล์แยกต่างหากและใช้ require / import เพื่อแยกส่วนประกอบจาก "service"

สิ่งนี้ช่วยให้การฉีดพึ่งพาและการทดสอบหน่วยของทั้งสองอย่างอิสระ


1

หรือคุณสามารถแทรกการสืบทอดคลาส "http" ลงใน React Component

ผ่านอุปกรณ์ประกอบฉาก

  1. อัปเดต:

    ReactDOM.render(<ReactApp data={app} />, document.getElementById('root'));
  2. เพียงแก้ไข React Component ReactApp เช่นนี้:

    class ReactApp extends React.Component {
    
    state = {
    
        data: ''
    
    }
    
        render(){
    
        return (
            <div>
            {this.props.data.getData()}      
            </div>
    
        )
        }
    }

0

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

hooks/useForm.js

เช่นถ้าคุณต้องการตรวจสอบความถูกต้องของข้อมูลแบบฟอร์มแล้วฉันจะสร้าง hook แบบกำหนดเองชื่อuseForm.jsและให้ข้อมูลในแบบฟอร์มและในทางกลับกันมันจะคืนค่าวัตถุที่มีสองสิ่งให้ฉัน:

Object: {
    value,
    error,
}

คุณสามารถส่งคืนสิ่งต่าง ๆ ได้มากขึ้นเมื่อคุณดำเนินการ

utils/URL.js

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

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