บทช่วยสอนเกี่ยวกับปฏิกิริยา: ส่วนประกอบ ตะขอ และประสิทธิภาพ

เผยแพร่แล้ว: 2022-03-11

ตามที่ได้อธิบายไว้ในส่วนแรกของการสอนเกี่ยวกับ React การเริ่มต้นใช้งาน React นั้นค่อนข้างง่าย เริ่มต้นด้วยการใช้ Create React App (CRA) เริ่มโครงการใหม่ และเริ่มพัฒนา น่าเศร้า เมื่อเวลาผ่านไป คุณอาจพบกับสถานการณ์ที่โค้ดของคุณค่อนข้างจะดูแลรักษายาก โดยเฉพาะอย่างยิ่งหากคุณยังใหม่ต่อ React ส่วนประกอบอาจใหญ่เกินความจำเป็นหรือคุณอาจจบลงด้วยองค์ประกอบที่อาจเป็นส่วนประกอบแต่ไม่ใช่ ดังนั้นคุณสามารถลงเอยด้วยการเขียนโค้ดซ้ำๆ ได้ที่นี่และที่นั่น

นั่นคือสิ่งที่คุณควรพยายามเริ่มต้นการเดินทางของ React โดยเริ่มคิดในโซลูชันการพัฒนา React

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

บทช่วยสอนเกี่ยวกับปฏิกิริยา: ภาพประกอบของส่วนประกอบปฏิกิริยา

นอกจากนี้ หากองค์ประกอบยาวกว่า สมมุติว่าความสูงของหน้าต่าง 2-3 อัน บางทีก็ควรแยก (ถ้าเป็นไปได้)—เพราะจะอ่านง่ายกว่าในภายหลัง

ส่วนประกอบที่ควบคุมเทียบกับที่ไม่มีการควบคุมใน React

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

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

 class ControlledInput extends React.Component { state = { value: "" }; onChange = (e) => this.setState({ value: e.target.value }); render() { return ( <input value={this.state.value} onChange={this.onChange}/> ); } }

ในส่วนประกอบ React ที่ไม่มีการควบคุม เราไม่สนใจว่าค่าจะเปลี่ยนไปอย่างไร แต่ถ้าเราต้องการทราบค่าที่แน่นอน เราเพียงแค่เข้าถึงผ่านการอ้างอิง

 class UncontrolledInput extends React.Component { input = React.createRef(); getValue = () => { console.log(this.input.current.value); }; render() { return ( <input ref={this.input}/> ); } }

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

อ้างอิง

เราได้กล่าวถึง refs ซึ่งเป็นคุณสมบัติพิเศษที่มีอยู่ในส่วนประกอบคลาสจนกระทั่ง hooks ปรากฏใน 16.8

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

ผู้อ้างอิงยังมีสามวิธีที่สามารถทำได้:

  • การใช้ตัวอักษรสตริง (แบบเดิมและควรหลีกเลี่ยง)
  • การใช้ฟังก์ชันเรียกกลับที่ตั้งค่าไว้ในแอตทริบิวต์ผู้อ้างอิง
  • โดยการสร้าง ref เป็น React.createRef() และผูกเข้ากับคุณสมบัติของคลาสและเข้าถึงได้ (โปรดทราบว่าการอ้างอิงจะพร้อมใช้งานจากวงจรชีวิต componentDidMount)

สุดท้าย มีบางกรณีที่ผู้อ้างอิงไม่ส่งต่อและบางครั้งที่คุณต้องการเข้าถึงองค์ประกอบอ้างอิงที่ลึกกว่าจากองค์ประกอบปัจจุบัน (เช่น คุณมีองค์ประกอบ <Button> ที่มีองค์ประกอบ DOM <input> ภายใน และตอนนี้คุณ อยู่ในองค์ประกอบ <Row> และจากองค์ประกอบแถวที่คุณต้องการให้เข้าถึงเพื่อป้อนฟังก์ชันโฟกัส DOM นั่นคือที่ที่คุณจะใช้ forwardRef )

กรณีหนึ่งที่การอ้างอิงไม่ถูกส่งต่อคือเมื่อมี ส่วนประกอบลำดับที่สูงกว่า ถูกใช้ในส่วนประกอบ—เหตุผลนั้นค่อนข้างเข้าใจได้เนื่องจากการ ref ไม่ใช่อุปกรณ์ประกอบ prop (คล้ายกับ key ) ดังนั้นจึงไม่ถูกส่งต่อดังนั้นมันจะ กำลังอ้างอิง HOC แทนส่วนประกอบที่ห่อหุ้มไว้ ในกรณีเช่นนี้ เราสามารถใช้ React.forwardRef ซึ่งรับ props และ refs เป็นอาร์กิวเมนต์ ซึ่งสามารถกำหนดให้กับ prop และส่งต่อไปยังส่วนประกอบที่เราต้องการเข้าถึงได้

 function withNewReference(Component) { class Hoc extends React.Component { render() { const {forwardedRef, ...props} = this.props; return <Component ref={forwardedRef} {...props}/>; } } return React.forwardRef((props, ref) => { return <Hoc {...props} forwardedRef={ref} />; }); }

ขอบเขตข้อผิดพลาด

ยิ่งมีสิ่งที่ซับซ้อนมากเท่าไหร่ โอกาสที่บางสิ่งจะผิดพลาดก็จะยิ่งสูงขึ้น นั่นเป็นเหตุผลที่ ขอบเขตข้อผิดพลาด เป็นส่วนหนึ่งของ React ดังนั้นพวกเขาทำงานอย่างไร?

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

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

ขอบเขตข้อผิดพลาดยังเป็นที่ที่คุณสามารถส่งข้อมูลไปยัง Error Logger ที่คุณใช้ (ในวิธี componentDidCatch lifecycle)

 class ErrorBoundary extends React.Component { state = { hasError: false }; static getDerivedStateFromError(error) { return { hasError: true }; } componentDidCatch(error, info) { logToErrorLogger(error, info); } render() { if (this.state.hasError) { return <div>Help, something went wrong.</div>; } return this.props.children; } }

ส่วนประกอบการสั่งซื้อที่สูงขึ้น

Higher Order Components (HOC) มักถูกกล่าวถึงใน React และเป็นรูปแบบที่ได้รับความนิยมอย่างมาก ซึ่งคุณอาจจะใช้ (หรือเคยใช้ไปแล้ว) หากคุณคุ้นเคยกับ HOCs คุณอาจเคยเห็น withNavigation, connect, withRouter ในไลบรารีหลายแห่ง

HOCs เป็นเพียงฟังก์ชันที่ใช้ส่วนประกอบเป็นอาร์กิวเมนต์ และจะส่งคืนส่วนประกอบใหม่พร้อมความสามารถเพิ่มเติมเมื่อเทียบกับส่วนประกอบที่ไม่มี HOC wrapper ด้วยเหตุนี้ คุณจึงสามารถบรรลุฟังก์ชันที่ขยายได้ง่ายบางฟังก์ชัน ซึ่งสามารถปรับปรุงส่วนประกอบของคุณ (เช่น การเข้าถึงการนำทาง) HOCs ยังสามารถเรียกรูปแบบสองสามรูปแบบขึ้นอยู่กับสิ่งที่เรามี อาร์กิวเมนต์เดียวที่จำเป็นเสมอว่าเป็นส่วนประกอบ แต่อาจมีอาร์กิวเมนต์พิเศษ—บางตัวเลือก หรือเหมือนในการ connect ก่อนอื่นคุณต้องเรียกใช้ฟังก์ชันด้วยการกำหนดค่าที่ส่งคืนฟังก์ชันในภายหลัง ซึ่งรับองค์ประกอบอาร์กิวเมนต์และส่งคืน HOC

มีบางสิ่งที่คุณสามารถเพิ่มและควรหลีกเลี่ยง:

  • เพิ่มชื่อที่แสดงสำหรับฟังก์ชัน wrapper HOC ของคุณ (เพื่อให้คุณรู้ว่าจริง ๆ แล้วเป็น HOC โดยการเปลี่ยนชื่อที่แสดงองค์ประกอบ HOC ของคุณ)
  • อย่าใช้ HOC ภายในวิธีการเรนเดอร์ คุณควรใช้ส่วนประกอบที่ปรับปรุงแล้วอยู่ภายใน แทนที่จะสร้างส่วนประกอบ HOC ใหม่ที่นั่น เนื่องจากต้องเมาต์ใหม่ตลอดเวลาและสูญเสียสถานะปัจจุบัน
  • วิธีสแตติกจะไม่ถูกคัดลอกทับ ดังนั้นหากคุณต้องการมีวิธีสแตติกบางอย่างภายใน HOC ที่สร้างขึ้นใหม่ คุณต้องคัดลอกด้วยตนเอง
  • Refs ที่กล่าวถึงจะไม่ผ่าน ดังนั้นใช้ React.forwardRef ตามที่กล่าวไว้ก่อนหน้านี้สำหรับการแก้ปัญหาดังกล่าว
 export function importantHoc() { return (Component) => class extends React.Component { importantFunction = () => { console.log("Very Important Function"); }; render() { return ( <Component {...this.props} importantFunction={this.importantFunction} /> ); } }; }

จัดแต่งทรงผม

การจัดสไตล์ไม่จำเป็นต้องเกี่ยวข้องกับ React เสมอไป แต่ก็ควรค่าแก่การกล่าวถึงด้วยเหตุผลหลายประการ

ก่อนอื่น สไตล์ CSS/อินไลน์ปกติจะใช้ที่นี่ตามปกติ และคุณสามารถเพิ่มชื่อคลาสจาก CSS ในแอตทริบิวต์ className ได้ และจะทำงานได้อย่างถูกต้อง สไตล์อินไลน์แตกต่างจากสไตล์ HTML ปกติเล็กน้อย สตริงจะไม่ถูกส่งต่อด้วยสไตล์ แต่เป็นออบเจ็กต์ที่มีค่าที่ถูกต้องสำหรับแต่ละรายการ คุณลักษณะของสไตล์ยังเป็น camelCased ดังนั้น border-radius จะกลายเป็น borderRadius เป็นต้น

ดูเหมือนว่า React จะได้รับความนิยมในโซลูชันสองสามอย่างที่กลายเป็นเรื่องธรรมดา ไม่เพียงแต่ใน React เช่น โมดูล CSS ที่เพิ่งรวมเข้ากับ CRA ซึ่งคุณสามารถนำเข้า name.modules.css และใช้คลาสเช่นคุณสมบัติเพื่อจัดรูปแบบองค์ประกอบของคุณ (IDE บางตัว) เช่น WebStorm มีการเติมข้อความอัตโนมัติด้วย ซึ่งจะบอกคุณว่ามีชื่อใดบ้าง)

โซลูชันอื่นที่เป็นที่นิยมใน React ก็คือ CSS-in-JS (เช่น ไลบรารี emotion ) เพียงเพื่อจะชี้ให้เห็นอีกครั้ง โมดูล CSS และอารมณ์ (หรือ CSS-in-JS โดยทั่วไป) ไม่ได้จำกัดอยู่ ที่ React

ตะขอใน React

Hooks เป็นส่วนเสริมที่รอคอยอย่างใจจดใจจ่อที่สุดใน React นับตั้งแต่การเขียนใหม่ ผลิตภัณฑ์ใช้งานได้จริงหรือไม่? จากมุมมองของฉัน ใช่ เนื่องจากเป็นคุณลักษณะที่ยอดเยี่ยมจริงๆ เป็นหน้าที่หลักที่เปิดโอกาสใหม่ ๆ เช่น:

  • อนุญาตให้ลบองค์ประกอบ class จำนวนมากที่เราใช้เพียงเพราะเราไม่สามารถมีได้ เช่น สถานะท้องถิ่นหรือการอ้างอิง ดังนั้นโค้ดสำหรับส่วนประกอบจึงดูอ่านง่ายขึ้น
  • ช่วยให้คุณใช้โค้ดน้อยลงสำหรับเอฟเฟกต์เดียวกัน
  • ทำให้ฟังก์ชันต่างๆ ง่ายต่อการคิดและทดสอบ เช่น โดยใช้ react-testing-library
  • สามารถรับพารามิเตอร์ได้เช่นกัน และผลลัพธ์ของหนึ่งสามารถใช้โดย hook อื่นได้อย่างง่ายดาย (เช่น setState จาก useState ใน useEffect )
  • ลดขนาดได้ดีกว่าคลาส ซึ่งมักจะเป็นปัญหาเล็กน้อยสำหรับตัวลดขนาด
  • อาจลบ HOC และแสดงรูปแบบอุปกรณ์ประกอบฉากในแอปของคุณ ซึ่งทำให้เกิดปัญหาใหม่ แม้ว่าจะได้รับการออกแบบมาเพื่อแก้ปัญหาอื่นๆ
  • สามารถสร้างขึ้นเองโดยนักพัฒนา React ที่มีทักษะ

มี React hooks ไม่กี่ตัวที่รวมไว้เป็นค่าเริ่มต้น พื้นฐานสามอย่างคือ useState , useEffect และ useContext นอกจากนี้ยังมีอีกหลายอย่าง เช่น useRef และ useMemo แต่สำหรับตอนนี้ เราจะเน้นที่พื้นฐาน

ลองมาดูที่ useState และลองใช้มันเพื่อสร้างตัวอย่างของตัวนับตรงไปตรงมา มันทำงานอย่างไร? โดยพื้นฐานแล้ว โครงสร้างทั้งหมดตรงไปตรงมาและดูเหมือนว่า:

 export function Counter() { const [counter, setCounter] = React.useState(0); return ( <div> {counter} <button onClick={() => setCounter(counter + 1)}>+</button> </div> ); };

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

นอกจากนี้ เนื่องจากส่วนประกอบดังกล่าวเคยถูกเรียกว่าส่วนประกอบที่ทำงานแบบไร้สัญชาติ ชื่อดังกล่าวจึงไม่เหมาะสมอีกต่อไป เนื่องจากส่วนประกอบดังกล่าวสามารถมีสถานะดังที่แสดงไว้ด้านบนได้ ดังนั้น ส่วนประกอบของคลาสชื่อและส่วนประกอบ ฟังก์ชัน จึงดูสอดคล้องกับสิ่งที่พวกเขาทำจริงมากกว่า อย่างน้อยจาก 16.8.0

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

 <button onClick={() => setCounter(prevCounter => prevCounter + 1)}>+</button> <button onClick={() => setCounter(prevCounter => prevCounter - 1)}>-</button>

อย่างไรก็ตาม ไม่เหมือนกับองค์ประกอบคลาส this.setState ซึ่งทำการผสานแบบตื้น การตั้งค่าฟังก์ชัน ( setCounter ในกรณีของเรา) จะแทนที่สถานะทั้งหมดแทน

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

 const [counter, setCounter] = useState(() => calculateComplexInitialValue());

สุดท้าย หากเราจะใช้ setCounter ด้วยค่าเดียวกันกับที่เรามีในขณะเดียวกันในสถานะปัจจุบัน ( counter ) ส่วนประกอบ จะไม่ แสดงผล

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

 const fetchApi = async () => { const value = await fetch("https://jsonplaceholder.typicode.com/todos/1"); console.log(await value.json()); }; export function Counter() { const [counter, setCounter] = useState(0); useEffect(() => { fetchApi(); }, []); return ( <div> {counter} <button onClick={() => setCounter(prevCounter => prevCounter + 1)}>+</button> <button onClick={() => setCounter(prevCounter => prevCounter - 1)}>-</button> </div> ); };

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

useContext ดูเหมือนจะเข้าใจง่ายที่สุด เนื่องจากบริบทหนึ่งระบุว่าเราต้องการเข้าถึงบริบทใด (วัตถุที่ส่งคืนโดยฟังก์ชัน createContext ) และในทางกลับกัน มันให้ค่าสำหรับบริบทนั้นแก่เรา

 const context = useContext(Context);

สุดท้าย เมื่อต้องการเขียนฮุคของคุณเอง คุณเพียงแค่เขียนสิ่งต่อไปนี้:

 function useWindowWidth() { let [windowWidth, setWindowWidth] = useState(window.innerWidth); function handleResize() { setWindowWidth(window.innerWidth); } useEffect(() => { window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); return windowWidth; }

โดยทั่วไป เราใช้ hook ของ useState ปกติที่เรากำหนดเป็นค่าเริ่มต้นของความกว้างของหน้าต่าง จากนั้นใน useEffect, เรากำลังเพิ่ม Listener ซึ่งจะทริกเกอร์ handleResize ในการปรับขนาดหน้าต่างแต่ละอัน นอกจากนี้เรายังล้างหลังจากส่วนประกอบจะถูกยกเลิกการต่อเชื่อม (ดูที่การส่งคืนใน useEffect ) ง่าย?

หมายเหตุ: การ ใช้ คำในตะขอทั้งหมดมีความสำคัญ ใช้เพราะอนุญาตให้ React ตรวจสอบว่าคุณไม่ได้ทำอะไรที่ไม่ดีหรือไม่ เช่น call hooks จากฟังก์ชัน JS ปกติ

การตรวจสอบประเภท

React มีการตรวจสอบอุปกรณ์ประกอบฉากของตัวเอง ก่อนที่ Flow และ TypeScript จะเป็นตัวเลือก

PropTypes ตรวจสอบว่าคุณสมบัติ (props) ที่ได้รับจากส่วนประกอบ React และตรวจสอบว่าสอดคล้องกับสิ่งที่เรามีหรือไม่ เมื่อใดก็ตามที่สถานการณ์ที่แตกต่างกันเกิดขึ้น (เช่น อ็อบเจ็กต์แทนที่จะเป็นอาร์เรย์) เราจะได้รับคำเตือนในคอนโซล สิ่งสำคัญที่ควรทราบคือ PropTypes ได้รับการตรวจสอบในโหมดการพัฒนาเท่านั้น เนื่องจากมีผลกระทบต่อประสิทธิภาพและคำเตือนคอนโซลดังกล่าว

ณ React 15.5 PropTypes อยู่ในแพ็คเกจอื่นที่ต้องติดตั้งแยกต่างหาก มีการประกาศตามคุณสมบัติในคุณสมบัติคงที่ที่เรียกว่า propTypes (แปลกใจ) รวมกับ defaultProps ซึ่งใช้หากคุณสมบัติไม่ได้กำหนดไว้ ( undefined เป็นกรณีเดียว) DefaultProps ไม่เกี่ยวข้องกับ PropTypes แต่สามารถแก้ไขคำเตือนบางอย่างที่อาจปรากฏขึ้นเนื่องจาก PropTypes

อีกสองตัวเลือกคือ Flow และ TypeScript และเป็นที่นิยมมากขึ้นในปัจจุบัน (โดยเฉพาะ TypeScript)

  • TypeScript เป็นชุดซูเปอร์เซ็ตของ JavaScript ที่พัฒนาโดย Microsoft ซึ่งสามารถตรวจสอบข้อผิดพลาดก่อนที่แอปจะทำงานและให้ฟังก์ชันการเติมข้อความอัตโนมัติที่ยอดเยี่ยมสำหรับการพัฒนา นอกจากนี้ยังปรับปรุงการปรับโครงสร้างใหม่อย่างมาก เนื่องจากการสนับสนุนของ Microsoft ซึ่งมีประสบการณ์มากมายกับภาษาที่พิมพ์ มันจึงเป็นตัวเลือกที่ค่อนข้างปลอดภัยเช่นกัน
  • Flow ไม่ใช่ภาษา ต่างจาก TypeScript เป็นตัวตรวจสอบประเภทสแตติกสำหรับ JavaScript ดังนั้นจึงคล้ายกับเครื่องมือที่คุณรวมไว้ใน JavaScript มากกว่าภาษา แนวคิดทั้งหมดที่อยู่เบื้องหลัง Flow ค่อนข้างคล้ายกับที่ TypeScript นำเสนอ ช่วยให้คุณเพิ่มประเภทได้ ดังนั้นจึงมีโอกาสน้อยที่จะมีข้อบกพร่องใดๆ ก่อนที่คุณจะเรียกใช้โค้ด เช่นเดียวกับ TypeScript ตอนนี้ Flow ได้รับการสนับสนุนใน CRA (สร้างแอป React) ตั้งแต่เริ่มต้น

โดยส่วนตัวแล้ว ฉันพบว่า TypeScript เร็วขึ้น (แทบจะในทันที) โดยเฉพาะอย่างยิ่งในการเติมข้อความอัตโนมัติ ซึ่ง Flow ดูเหมือนจะช้าลงเล็กน้อย เป็นที่น่าสังเกตว่า IDE เช่น WebStorm ซึ่งฉันใช้เป็นการส่วนตัว ใช้ CLI สำหรับการผสานรวมกับ Flow อย่างไรก็ตาม ดูเหมือนง่ายยิ่งขึ้นที่จะรวมการใช้งานทางเลือกในไฟล์ โดยที่คุณเพียงแค่เพิ่ม // @flow ที่จุดเริ่มต้นของไฟล์เพื่อเริ่มการตรวจสอบประเภท นอกจากนี้ จากสิ่งที่ฉันสามารถบอกได้ ดูเหมือนว่า TypeScript จะชนะการต่อสู้กับ Flow ในตอนท้าย—มันเป็นที่นิยมมากกว่าในตอนนี้ และไลบรารียอดนิยมบางตัวได้รับการปรับโครงสร้างใหม่จาก Flow เป็น TypeScript

มีตัวเลือกเพิ่มเติมสองสามตัวที่กล่าวถึงในเอกสารประกอบอย่างเป็นทางการ เช่น เหตุผล (พัฒนาโดย Facebook และกำลังได้รับความนิยมในชุมชน React), Kotlin (ภาษาที่พัฒนาโดย JetBrains) และอื่นๆ

แน่นอน สำหรับนักพัฒนา front-end วิธีที่ง่ายที่สุดคือเข้าร่วมและเริ่มใช้ Flow และ TypeScript แทนที่จะเปลี่ยนไปใช้ Kotlin หรือ F# อย่างไรก็ตาม สำหรับนักพัฒนาแบ็คเอนด์ที่กำลังเปลี่ยนไปใช้ฟรอนต์เอนด์ สิ่งเหล่านี้อาจเริ่มต้นได้ง่ายกว่าจริง

ประสิทธิภาพการผลิตและการตอบสนอง

การเปลี่ยนแปลงขั้นพื้นฐานและชัดเจนที่สุดที่คุณต้องทำสำหรับโหมดการผลิตคือการเปลี่ยนไปใช้ "การผลิต" สำหรับ DefinePlugin และเพิ่ม UglifyJsPlugin ในกรณีของ Webpack ในกรณีของ CRA มันง่ายเหมือนกับการใช้ npm run build (ซึ่งจะรัน react-scripts build ) โปรดทราบว่า Webpack และ CRA ไม่ใช่ตัวเลือกเดียว เนื่องจากคุณสามารถใช้เครื่องมือสร้างอื่นๆ เช่น Brunch ได้ นี้มักจะครอบคลุมในเอกสารอย่างเป็นทางการ ไม่ว่าจะเป็นเอกสาร React อย่างเป็นทางการหรือเอกสารสำหรับเครื่องมือเฉพาะ เพื่อให้แน่ใจว่าโหมดได้รับการตั้งค่าอย่างถูกต้อง คุณสามารถใช้ React Developer Tools ซึ่งจะช่วยให้คุณทราบว่าคุณกำลังใช้บิลด์ประเภทใด (การผลิตกับการพัฒนา) ขั้นตอนดังกล่าวจะทำให้แอปพลิเคชันของคุณทำงานโดยไม่มีการตรวจสอบและคำเตือนที่มาจาก React และบันเดิลเองก็จะถูกย่อให้เล็กสุดเช่นกัน

มีอีกสองสามสิ่งที่คุณสามารถทำได้สำหรับแอป React ของคุณ คุณทำอะไรกับไฟล์ JS ที่สร้างขึ้น? คุณสามารถเริ่มต้นด้วยเพียงแค่ “bundle.js” หากขนาดค่อนข้างเล็ก หรืออาจทำบางอย่างเช่น “ผู้ขาย + บันเดิล” หรือบางที “ผู้ขาย + ส่วนที่เล็กที่สุดที่จำเป็น + นำเข้าสิ่งของเมื่อจำเป็น” สิ่งนี้มีประโยชน์เมื่อคุณต้องรับมือกับแอพขนาดใหญ่ และคุณไม่จำเป็นต้องนำเข้าทุกอย่างตั้งแต่เริ่มต้น โปรดทราบว่าการรวมโค้ด JavaScript บางตัวในชุดหลักที่ไม่ได้ใช้งานจะเพิ่มขนาดบันเดิลและทำให้แอปโหลดช้าลงในตอนเริ่มต้น

บันเดิลผู้ขายจะมีประโยชน์หากคุณกำลังวางแผนที่จะหยุดเวอร์ชันของไลบรารี่โดยตระหนักว่าอาจไม่เปลี่ยนแปลงเป็นเวลานาน (ถ้ามี) นอกจากนี้ ไฟล์ที่ใหญ่กว่าจะดีกว่าในการ gzipping ดังนั้นประโยชน์ที่คุณได้รับจากการแยกในบางครั้งอาจไม่คุ้มค่า ขึ้นอยู่กับขนาดไฟล์และบางครั้งคุณก็ต้องลองด้วยตัวเอง

การแยกรหัส

การแยกรหัสสามารถปรากฏได้หลายวิธีมากกว่าที่แนะนำไว้ที่นี่ แต่มาเน้นที่สิ่งที่เรามีให้ใน CRA และ React กันดีกว่า โดยพื้นฐานแล้ว เพื่อที่จะแยกโค้ดเป็นส่วนๆ เราสามารถใช้ import() ซึ่งใช้งานได้ดีกับ Webpack ( import นั้นเป็นข้อเสนอในด่านที่ 3 ณ ตอนนี้ ดังนั้นจึงยังไม่เป็นส่วนหนึ่งของมาตรฐานภาษา) เมื่อใดก็ตามที่ Webpack เห็น import มันจะรู้ว่าจำเป็นต้องเริ่มการแยกโค้ดในขั้นตอนนี้ และไม่สามารถรวมไว้ในบันเดิลหลักได้ (โค้ดที่อยู่ในการนำเข้า)

ตอนนี้ เราสามารถเชื่อมต่อกับ React.lazy() ซึ่งต้องการ import() ด้วยพาธไฟล์ที่มีส่วนประกอบที่ต้องแสดงผลในที่นั้น ต่อไป เราสามารถใช้ React.suspense() ซึ่งจะแสดงส่วนประกอบอื่นในตำแหน่งนั้นจนกว่าจะโหลดส่วนประกอบที่นำเข้า อาจมีคนสงสัยว่า ถ้าเรานำเข้าส่วนประกอบเดียว เหตุใดเราจึงต้องการมัน

นั่นไม่ใช่กรณีทั้งหมด เนื่องจาก React.lazy() จะแสดงส่วนประกอบที่เรา import() แต่ import() อาจดึงชิ้นส่วนที่ใหญ่กว่าส่วนประกอบเดียวนั้น ตัวอย่างเช่น คอมโพเนนต์เฉพาะนั้นอาจมีไลบรารีอื่น ๆ ในการพ่วง โค้ดเพิ่มเติม ฯลฯ ดังนั้นไฟล์เดียวจึงไม่จำเป็น — อาจเป็นไฟล์จำนวนมากที่รวมเข้าด้วยกัน สุดท้าย เราสามารถรวมทั้งหมดนั้นไว้ใน ErrorBoundary (คุณสามารถหาโค้ดได้ในส่วนของเราเกี่ยวกับขอบเขตข้อผิดพลาด) ซึ่งจะทำหน้าที่เป็นทางเลือกสำรองหากมีบางอย่างล้มเหลวกับส่วนประกอบที่เราต้องการนำเข้า (เช่น หากมีข้อผิดพลาดของเครือข่าย)

 import ErrorBoundary from './ErrorBoundary'; const ComponentOne = React.lazy(() => import('./ComponentOne')); function MyComponent() { return ( <ErrorBoundary> <React.Suspense fallback={<div>Loading...</div>}> <ComponentOne/> </React.Suspense> </ErrorBoundary> ); }

นี่เป็นตัวอย่างพื้นฐาน แต่คุณสามารถทำอะไรได้มากกว่านั้นอย่างเห็นได้ชัด คุณสามารถใช้ import และ React.lazy สำหรับการแยกเส้นทางแบบไดนามิก (เช่น ผู้ดูแลระบบเทียบกับผู้ใช้ทั่วไป หรือแค่เส้นทางขนาดใหญ่จริงๆ โปรดทราบว่า React.lazy รองรับเฉพาะการส่งออกเริ่มต้น ณ ตอนนี้ และไม่รองรับการแสดงผลฝั่งเซิร์ฟเวอร์

ประสิทธิภาพของโค้ดตอบสนอง

เกี่ยวกับประสิทธิภาพ หากแอปพลิเคชัน React ของคุณทำงานล่าช้า มีเครื่องมือสองอย่างที่สามารถช่วยคุณแก้ปัญหาได้

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

อีกทางเลือกหนึ่งคือใช้ DevTools Profiler ซึ่งมีให้ใช้งานใน React 16.5+ และด้วยความร่วมมือจาก shouldComponentUpdate (หรือ PureComponent ซึ่งได้อธิบายไว้ในส่วนที่หนึ่งของบทช่วยสอนนี้) เราสามารถปรับปรุงประสิทธิภาพสำหรับส่วนประกอบที่สำคัญบางอย่างได้

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

สถานะของปฏิกิริยาในปี 2019 และปีต่อๆ ไป

ถ้าเราจะพูดถึงอนาคตของ React โดยส่วนตัวฉันจะไม่กังวลมากนัก จากมุมมองของฉัน React จะไม่มีปัญหาในการรักษาบัลลังก์ในปี 2019 และปีต่อๆ ไป

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

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

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

สุดท้ายนี้ นั่นคือสิ่งที่ฉันได้ทำไปเมื่อเร็วๆ นี้ นั่นคือ React Native สำหรับฉัน มันเป็นเทคโนโลยีที่ยอดเยี่ยมที่เปลี่ยนแปลงไปมากในช่วงสองสามปีที่ผ่านมา (การขาดลิงก์แบบโต้ตอบดั้งเดิมอาจเป็นปัญหาที่ใหญ่ที่สุดสำหรับคนส่วนใหญ่ และเห็นได้ชัดว่ามีข้อบกพร่องมากมาย) React Native กำลังได้รับการเขียนใหม่หลัก และนั่นก็ควรจะทำในลักษณะเดียวกันกับการเขียนซ้ำของ React (ทั้งหมดเป็นเรื่องภายใน ไม่มีอะไรหรือแทบไม่ควรเปลี่ยนสำหรับนักพัฒนาเลย) การเรนเดอร์แบบอะซิงโครนัส สะพานเชื่อมที่เร็วขึ้นและเบายิ่งขึ้นระหว่างเนทีฟและ JavaScript และอื่นๆ

มีอะไรให้ตั้งตารอมากมายในระบบนิเวศ React แต่การอัพเดท hooks (และ React Native หากใครรักมือถือ) น่าจะเป็นการเปลี่ยนแปลงที่สำคัญที่สุดที่เราจะได้เห็นในปี 2019

ที่เกี่ยวข้อง: ข้อมูลเก่าขณะตรวจสอบการดึงข้อมูลด้วย React Hooks: A Guide