หน้าที่ในส่วนประกอบไร้สัญชาติ?


93

ฉันกำลังพยายามแปลง<canvas>แอนิเมชั่นสุดเจ๋งที่ฉันพบที่นี่ให้เป็นส่วนประกอบที่สามารถนำกลับมาใช้ใหม่ได้ ดูเหมือนว่าคอมโพเนนต์นี้ต้องการคอมโพเนนต์พาเรนต์หนึ่งสำหรับแคนวาสและคอมโพเนนต์ลูกจำนวนมากสำหรับไฟล์function Ball().

ด้วยเหตุผลด้านประสิทธิภาพอาจเป็นการดีกว่าที่จะทำให้Ballsเป็นส่วนประกอบที่ไร้สัญชาติเนื่องจากจะมีหลายองค์ประกอบ ฉันไม่คุ้นเคยกับการสร้างส่วนประกอบที่ไร้สัญชาติและสงสัยว่าฉันควรกำหนดthis.update()และthis.drawฟังก์ชันที่กำหนดไว้ที่function Ball()ใด

ฟังก์ชันสำหรับส่วนประกอบที่ไม่มีสถานะอยู่ภายในส่วนประกอบหรือภายนอก? กล่าวอีกนัยหนึ่งข้อใดต่อไปนี้ดีกว่า

1:

const Ball = (props) => {
    const update = () => {
        ...
    }

    const draw = () => {
        ...
    }

    return (
       ...
    );
}

2:

function update() {
     ...
}

function draw() {
     ...
}

const Ball = (props) => {
    return (
       ...
    );
}

ข้อดี / ข้อเสียของแต่ละข้อคืออะไรและหนึ่งในนั้นดีกว่าสำหรับกรณีการใช้งานเฉพาะเช่นของฉันหรือไม่


คุณสามารถโพสต์รหัสที่มีอยู่เพื่อให้เราดูว่าจะใช้งานได้อย่างไร?
Scimonster

@Scimonster ฉันโพสต์ไว้ในลิงค์ที่ฝังไว้บางทีคุณอาจพลาดไป นี่คือลิงค์: codepen.io/awendland/pen/XJExGv
MarksCode

คำตอบ:


117

สิ่งแรกที่ควรทราบคือส่วนประกอบที่ใช้งานได้แบบไม่ระบุสถานะไม่สามารถมีเมธอดได้คุณไม่ควรพึ่งพาการโทรupdateหรือdrawการแสดงผลBallหากเป็นส่วนประกอบการทำงานที่ไม่มีสถานะ

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

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

// You can use update somewhere else
const update (propX, a, b) => { ... };

const Ball = props => (
  <Something onClick={update.bind(null, props.x)} />
);

หากคุณใช้hooksคุณสามารถใช้useCallbackเพื่อให้แน่ใจว่าฟังก์ชันได้รับการกำหนดใหม่ก็ต่อเมื่อมีprops.xการเปลี่ยนแปลงการอ้างอิงอย่างใดอย่างหนึ่ง ( ในกรณีนี้):

const Ball = props => {
  const onClick = useCallback((a, b) => {
    // do something with a, b and props.x
  }, [props.x]);

  return (
    <Something onClick={onClick} />
  );
}

นี่เป็นวิธีที่ผิด :

const Ball = props => {
  function update(a, b) {
    // props.x is visible here
  }

  return (
    <Something onClick={update} />
  );
}

เมื่อใช้useCallbackการกำหนดupdateฟังก์ชันในuseCallbackตัวเบ็ดเองภายนอกส่วนประกอบของเรากลายเป็นการตัดสินใจในการออกแบบมากกว่าสิ่งใด ๆ คุณควรคำนึงถึงว่าคุณจะนำกลับมาใช้ใหม่updateและ / หรือหากคุณต้องการเข้าถึงขอบเขตของการปิดของส่วนประกอบ ตัวอย่างเช่นอ่าน / เขียนถึงรัฐ โดยส่วนตัวแล้วฉันเลือกที่จะกำหนดมันภายในองค์ประกอบตามค่าเริ่มต้นและทำให้สามารถนำกลับมาใช้ใหม่ได้ก็ต่อเมื่อมีความจำเป็นเกิดขึ้นเพื่อป้องกันไม่ให้ over-engineering ตั้งแต่เริ่มต้น ยิ่งไปกว่านั้นการใช้ตรรกะของแอปพลิเคชันซ้ำนั้นทำได้ดีกว่าด้วย hooks ที่เฉพาะเจาะจงมากขึ้นโดยปล่อยให้ส่วนประกอบต่างๆเพื่อวัตถุประสงค์ในการนำเสนอ การกำหนดฟังก์ชันภายนอกส่วนประกอบในขณะที่ใช้ hooks นั้นขึ้นอยู่กับเกรดของการแยกจาก React ที่คุณต้องการสำหรับตรรกะแอปพลิเคชันของคุณ


ขอบคุณมาร์โกที่ทำให้ทุกอย่างชัดเจนขึ้น สิ่งที่ฉันสับสนในกรณีของฉันเกี่ยวข้องกับthis.drawฟังก์ชันภายในไฟล์Ball. ใช้ctxจากสิ่งที่จะเป็นของผู้ปกครอง<canvas>และยังใช้thisคำหลักสำหรับสิ่งที่จะเป็นBallองค์ประกอบลูก อะไรคือวิธีที่ดีที่สุดในการผสานรวมการใช้ส่วนประกอบที่ไร้สัญชาติเพื่อให้คุณสมบัติทั้งสองนั้นสามารถเข้าถึงได้
MarksCode

ไม่มีthisเมื่อใช้ส่วนประกอบการทำงานที่ไร้สถานะโปรดจำไว้ว่า สำหรับบริบทผืนผ้าใบคุณจะต้องส่งต่อไปยังทุก ๆ ซิงเกิ้ลBallซึ่งฟังดูไม่ดีเลย
Marco Scabbiolo

ดังนั้นในกรณีนี้จะเป็นการดีที่สุดที่จะไม่มีองค์ประกอบย่อยที่คุณกำลังพูด?
MarksCode

1
@MarcoScabbiolo ไม่นั่นไม่ใช่กรณีของฉันใช้ฟังก์ชันลูกศรมาเป็นเวลานานแล้วเนื่องจากเบราว์เซอร์เดียวที่ไม่รองรับคือ IE อันที่จริงฉันสามารถค้นหาความคิดเห็นนี้ได้จากบทความหนึ่งซึ่งมีการอ้างว่าbindโดยเฉพาะในChrome ก่อนหน้า 59นั้นช้ากว่าฟังก์ชันลูกศรด้วยซ้ำ และใน Firefox ก็ใช้เวลาพอสมควรเนื่องจากทั้งคู่ทำงานด้วยความเร็วเท่ากัน ดังนั้นฉันจะบอกว่าในกรณีเช่นนี้ไม่มีความแตกต่างอะไรคือวิธีที่ต้องการ :)
Vadim Kalinin

1
@ MauricioAvendañoไม่ว่าจะเป็นวิธีใดก็ตาม แต่เป็นการปฏิบัติที่ไม่ดีสำหรับSomethingองค์ประกอบที่จะรู้ว่ามี prop X ในองค์ประกอบหลักเนื่องจากทำให้ทราบบริบท สิ่งเดียวกันนี้เกิดขึ้นกับคำถามที่คุณถามและโค้ดตัวอย่างที่ฉันเขียนขึ้นอยู่กับบริบทซึ่งจะถูกละเว้นเพื่อความเรียบง่าย
Marco Scabbiolo

13

เราสามารถมีฟังก์ชั่นภายในส่วนประกอบการทำงานที่ไม่ระบุสถานะด้านล่างนี้คือตัวอย่าง:

 const Action = () => {
  function  handlePick(){
     alert("test");
  }
  return (
    <div>
      <input type="button" onClick={handlePick} value="What you want to do ?" />
    </div>
  );
}

แต่ไม่ใช่แนวทางปฏิบัติที่ดีเนื่องจากฟังก์ชันhandlePick()จะถูกกำหนดทุกครั้งเมื่อมีการเรียกส่วนประกอบ


11
หากไม่ใช่แนวทางปฏิบัติที่ดีทางเลือกอื่นจะเป็นอย่างไร?
John Samuel

@JohnSamuel ทางเลือกคือการกำหนดhandlePick()ด้านบน / นอกองค์ประกอบเช่นfunction handlePick() { ... }; const Action = () => { ... }ผลลัพธ์ที่ handlePick กำหนดเพียงครั้งเดียว หากคุณต้องการข้อมูลจากActionส่วนประกอบคุณควรส่งเป็นพารามิเตอร์ไปยังhandlePickฟังก์ชัน
James Hay

14
หากนี่ไม่ใช่แนวทางปฏิบัติที่ดีคุณสามารถเพิ่มแนวทางปฏิบัติที่ดีพร้อมคำตอบได้หรือไม่? ตอนนี้ฉันต้องค้นหาแนวทางปฏิบัติที่ดีอีกครั้ง :(
Shamseer Ahammed

2

เราสามารถใช้ React hook useCallbackดังต่อไปนี้ในส่วนประกอบการทำงาน:

const home = (props) => {
    const { small, img } = props
    const [currentInd, setCurrentInd] = useState(0);
    const imgArrayLength = img.length - 1;
    useEffect(() => {
        let id = setInterval(() => {
            if (currentInd < imgArrayLength) {
                setCurrentInd(currentInd => currentInd + 1)
            }
            else {
                setCurrentInd(0)
            }
        }, 5000);
        return () => clearInterval(id);
    }, [currentInd]);
    const onLeftClickHandler = useCallback(
        () => {
            if (currentInd === 0) {

            }
            else {
                setCurrentInd(currentInd => currentInd - 1)
            }
        },
        [currentInd],
    );

    const onRightClickHandler = useCallback(
        () => {
            if (currentInd < imgArrayLength) {
                setCurrentInd(currentInd => currentInd + 1)
            }
            else {

            }
        },
        [currentInd],
    );
    return (
        <Wrapper img={img[currentInd]}>
            <LeftSliderArrow className={currentInd > 0 ? "red" : 'no-red'} onClick={onLeftClickHandler}>
                <img src={Icon_dir + "chevron_left_light.png"}></img>
            </LeftSliderArrow>
            <RightSliderArrow className={currentInd < imgArrayLength ? "red" : 'no-red'} onClick={onRightClickHandler}>
                <img src={Icon_dir + "chevron_right_light.png"}></img>
            </RightSliderArrow>
        </Wrapper>);
}

export default home;

ฉันได้รับ 'img' จากพาเรนต์และนั่นคืออาร์เรย์


1

หากคุณต้องการใช้อุปกรณ์ประกอบฉากหรือสถานะของส่วนประกอบในฟังก์ชันควรกำหนดไว้ในองค์ประกอบด้วย useCallback

function Component(props){
  const onClick=useCallback(()=>{
     // Do some things with props or state
  },[])
}

ในทางกลับกันหากคุณไม่ต้องการใช้อุปกรณ์ประกอบฉากหรือสถานะในฟังก์ชันให้กำหนดสิ่งนั้นนอกองค์ประกอบ

const computeSomethings=()=>{
   // Do some things with params or side effects
}

function Component(props){
  
}

3
คุณสามารถยกตัวอย่างการใช้ hook สำหรับวิธีการภายในองค์ประกอบการทำงานได้หรือไม่? (not set state method)
jake-ferguson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.