วิธีใช้ React.memo เพื่อเพิ่มประสิทธิภาพ

Nov 21 2020

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

const Details = props => {
    console.log(props.status);
    let [backGround, setBackGround] = useState("w details")
    const history = useHistory();

    if (props.status.white && backGround === "bl details"){
        setBackGround("w details")
    } else if (!props.status.white && backGround === "w details"){
        setBackGround("bl details")
    }

ที่ต้องรับผิดชอบเพราะ console.log การพิมพ์อุปกรณ์ประกอบฉากจะพิมพ์คำสั่ง 8 ครั้งต่อการเคลื่อนไหว (แต่เดิมเป็นสองครั้ง)

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

React Hooks - ฉันจะติดตั้ง shouldComponentUpdate ได้อย่างไร

ฉันควรห่อส่วนประกอบทั้งหมดของฉันด้วย React.memo () หากไม่ได้คาดหวังอุปกรณ์ประกอบฉากใด ๆ

ใช้ shouldComponentUpdate กับ React Hooks อย่างไร?

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

รายละเอียด jsx

import React, { useState } from 'react';
import "../App.css"
import DataService from '../service/DataService';
import { useHistory } from 'react-router-dom';

let [backGround, setBackGround] = useState("w details")
const Details = React.memo(props => {if (props.status.white){setBackGround("w details")} else {setBackGround("bl details")}}, (prevProps, props) => prevProps.white === props.white);  {
    console.log(props.status);
    
    const history = useHistory();

    const undo = () => {
        DataService.undo()
        .then(res => {
            console.log(res);
            props.setTheBoard(res.data);
            props.changeTurn();
        })
        .catch(err => {
            console.log(err);
            window.alert(err.response.data.errMessage)
        })
    }
    
    const restart = () => {
        DataService.restartGame()
        .then(res => {
            console.log(res);
            props.setTheBoard(res.data);
            props.changeTurn(true);
        })
        .catch(err => {
            console.log(err);
            window.alert(err.response.data.errMessage)
        })
    }

    const newGame = () => {
        history.push('/');
    }

    return ( 
        <div className={backGround} >  
            {props.status.active ? <h2>It is {props.status.playerName}'s turn</h2> :
            <div>           
                <h1 className="check">Game Over!</h1>
                <button className="tooltip" onClick={restart}>RESTART<span className="tooltiptext">Play another game vs the same opponent</span></button>
                <button className="tooltip" onClick={newGame}>NEW GAME<span className="tooltiptext">Play a game vs a different opponent</span></button>
            </div>}                          
                       
                           
            {props.status.active &&
            <div>                
                {props.isMove ? <button className="detailButtons" onClick={props.specialMove}>Special Move</button> : <button className="detailButtons" onClick={() => props.endTheGame(true)}>Forfeit</button> }
                {props.isMove ? <button className="detailButtons" onClick={props.unselect}>Unselect Piece</button> : <button className="detailButtons" onClick={() => props.endTheGame(false)}>Draw</button> } 
                {props.isMove ? <button className="detailButtons">Toggle Sidebar</button> : props.undo && <button className="detailButtons" onClick={() => undo()}>Undo</button> }                
                {props.status.check && <h1 className="check">You must move out of check!</h1>}                
            </div> }
            
        </div>
     );
}
 
export default Details;

เนื่องจากอุปกรณ์ประกอบฉากในส่วนประกอบนี้จะเปลี่ยนเฉพาะเมื่อเทิร์นเปลี่ยนไป (props.status.white) ฉันคิดว่านี่น่าจะเป็นจุดที่ดีในการพยายามลดการแสดงผลซ้ำที่ไม่จำเป็น แต่วิธีแก้ปัญหาทั้งหมดที่ฉันเห็นว่ามันง่ายมาก มันเป็นไปไม่ได้ที่จะใช้ React.memo เมื่อมีการใช้อุปกรณ์ประกอบฉากเช่นนี้อย่างแพร่หลาย?

ฉันจะเพิ่มประสิทธิภาพในขณะที่ยังสามารถเข้าถึงอุปกรณ์ประกอบฉากได้อย่างไร

คำตอบ

4 JanCássio Nov 21 2020 at 02:31

อันดับแรกสิ่งที่คุณต้องคำนึงถึงเกี่ยวกับการทดสอบประสิทธิภาพในส่วนหน้าโดยทั่วไป

คุณจะไม่เปิด devtools ทิ้งไว้ในขณะที่คุณกำลังตรวจสอบประสิทธิภาพกิจกรรมต่างๆใน devtools ลดประสิทธิภาพของแอปของคุณได้มาก

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

ดังนั้นเราควรใช้สถานการณ์ใดuseCallbackและuseMemoเพื่อปรับปรุงประสิทธิภาพ

ก่อนจะตอบคุณควรทราบความแตกต่างระหว่างประเภทค่าและประเภทการอ้างอิง

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

ประเภทตัวอักษรดั้งเดิมใน JS เป็นประเภทค่า:

let a = "Hello"
let b = a
// from here, b doesn't own a reference, because a is a value type

let c = new String("Hello")
let d = c
// now d owns c reference because c was initialized as an instance of String class

นี่เป็นตัวอย่างง่ายๆของการสร้างสตริงเดียวกันโดยใช้สองวิธีที่แตกต่างกัน

อย่างแรกคือการสร้างสตริงโดยใช้สตริงตามตัวอักษร ประการที่สองโดยใช้อินสแตนซ์ของคลาส String

ถ้าเราทำอะไรแบบนั้น:

a === b // true

===เปรียบเทียบค่าของaกับค่าbใน

อย่างไรก็ตามสิ่งที่แตกต่างเกิดขึ้นเมื่อเราทำสิ่งนั้น:

c === d // true

นอกจากนี้ยังกลับมาtrueแต่===การทำงานที่แตกต่างกันที่นี่แทนค่าเปรียบเทียบก็เปรียบเทียบอ้างอิงของ c เป็นอย่างเคร่งครัดเท่ากับอ้างอิงง เราไม่สามารถเปรียบเทียบได้ว่าค่าของcนั้นเท่ากับค่าของการdใช้===ตัวดำเนินการอย่างเคร่งครัดหรือไม่เนื่องจากทั้งสองเป็นประเภทอ้างอิงของ String เราจึงควรเปรียบเทียบทั้งสองอย่าง:

// toString() in this case works as well but valueOf is more semantic in this case
c.valueOf() === d.valueOf()

คิดเกี่ยวกับaและbไม่ได้ชี้ไปอ้างอิงในหน่วยความจำ แต่dอ้างอิงถึงเดียวกันcอ้างอิงสร้างขึ้นใน

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

เพื่อช่วยในการตอบสนองเปรียบเทียบค่าอ้างอิงเช่นประเภทฟังก์ชันอาร์เรย์และวัตถุเราใช้useCallbackหรือuseMemoเพื่อห่อหุ้มการอ้างอิงเหล่านั้นให้เป็นประเภทใหม่ที่ React สามารถเปรียบเทียบและตัดสินใจว่าค่าของการอ้างอิงมีการเปลี่ยนแปลงหรือไม่

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

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

ในประวัติย่อuseMemoและuseCallbackใช้เพื่อช่วยตอบสนองเพื่อดูว่าการอ้างอิงบางประเภทมีการเปลี่ยนแปลงค่าเพื่อแสดงผลคอมโพเนนต์หรือไม่