จะบังคับให้ติดตั้งใหม่บนส่วนประกอบของปฏิกิริยาได้อย่างไร?


104

สมมติว่าฉันมีองค์ประกอบมุมมองที่มีการแสดงผลตามเงื่อนไข:

render(){
    if (this.state.employed) {
        return (
            <div>
                <MyInput ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div>
                <MyInput ref="unemployment-reason" name="unemployment-reason" />
                <MyInput ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

MyInput มีลักษณะดังนี้:

class MyInput extends React.Component {

    ...

    render(){
        return (
            <div>
                <input name={this.props.name} 
                    ref="input" 
                    type="text" 
                    value={this.props.value || null}
                    onBlur={this.handleBlur.bind(this)}
                    onChange={this.handleTyping.bind(this)} />
            </div>
        );
    }
}

ให้พูดemployedเป็นความจริง เมื่อใดก็ตามที่ฉันเปลี่ยนเป็นเท็จและมุมมองอื่น ๆ แสดงผลunemployment-durationจะถูกกำหนดค่าเริ่มต้นใหม่เท่านั้น นอกจากนี้ยังunemployment-reasonได้รับการเติมล่วงหน้าด้วยค่าจากjob-title(หากให้ค่าก่อนที่เงื่อนไขจะเปลี่ยนแปลง)

ถ้าฉันเปลี่ยนมาร์กอัปในรูทีนการเรนเดอร์ที่สองเป็นแบบนี้:

render(){
    if (this.state.employed) {
        return (
            <div>
                <MyInput ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div>
                <span>Diff me!</span>
                <MyInput ref="unemployment-reason" name="unemployment-reason" />
                <MyInput ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

ดูเหมือนว่าทุกอย่างจะทำงานได้ดี ดูเหมือนว่า React จะไม่สามารถแยกแยะ "ตำแหน่งงาน" และ "เหตุผลการว่างงาน" ได้

ช่วยบอกทีว่าฉันทำอะไรผิด ...


1
องค์ประกอบdata-reactidแต่ละอย่างMyInput(หรือinputตามที่เห็นใน DOM) ที่อยู่ก่อนและหลังemployedสวิตช์คืออะไร
คริส

@Chris ฉันไม่สามารถระบุได้ว่าส่วนประกอบ MyInput แสดงผลอินพุตที่ห่อด้วยไฟล์<div>. data-reactidคุณลักษณะที่ดูเหมือนว่าจะมีความแตกต่างกันทั้งใน div ห่อและช่องใส่ job-titleอินพุตได้รับ id data-reactid=".0.1.1.0.1.0.1"ในขณะที่unemployment-reasonอินพุตได้รับ iddata-reactid=".0.1.1.0.1.2.1"
o01

1
และสิ่งที่เกี่ยวกับunemployment-duration?
คริส

@ คริสขออภัยฉันพูดเร็วเกินไป ในตัวอย่างแรก (ไม่มีช่วง "diff me") reactidแอตทริบิวต์จะเหมือนกันjob-titleและunemployment-reasonในขณะที่ในตัวอย่างที่สอง (ที่มีช่วงต่าง) ต่างกัน
o01

@ Chris สำหรับแอตทริบิวต์อยู่เสมอที่ไม่ซ้ำกัน unemployment-durationreactid
01

คำตอบ:


109

สิ่งที่อาจเกิดขึ้นคือ React คิดว่ามีเพียงหนึ่งMyInput( unemployment-duration) เท่านั้นที่ถูกเพิ่มระหว่างการแสดงผล ด้วยเหตุนี้ค่าที่job-titleไม่เคยถูกแทนที่ด้วยunemployment-reasonซึ่งเป็นสาเหตุที่เปลี่ยนค่าที่กำหนดไว้ล่วงหน้า

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

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

นี่คือสิ่งที่เอกสารการตอบสนองอย่างเป็นทางการกล่าวว่า:

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

เมื่อ React กระทบลูกที่ถูกคีย์จะทำให้มั่นใจได้ว่าลูกที่มีคีย์จะถูกจัดลำดับใหม่ (แทนที่จะเป็นก้อน) หรือถูกทำลาย (แทนที่จะนำกลับมาใช้ใหม่)

คุณควรจะแก้ไขปัญหานี้ได้โดยให้keyองค์ประกอบที่ไม่ซ้ำกันด้วยตัวคุณเองให้กับผู้ปกครองdivหรือMyInputองค์ประกอบทั้งหมด

ตัวอย่างเช่น:

render(){
    if (this.state.employed) {
        return (
            <div key="employed">
                <MyInput ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div key="notEmployed">
                <MyInput ref="unemployment-reason" name="unemployment-reason" />
                <MyInput ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

หรือ

render(){
    if (this.state.employed) {
        return (
            <div>
                <MyInput key="title" ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div>
                <MyInput key="reason" ref="unemployment-reason" name="unemployment-reason" />
                <MyInput key="duration" ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

ตอนนี้เมื่อ React ทำการเปลี่ยนแปลงมันจะเห็นว่าสิ่งdivsที่แตกต่างกันและจะแสดงผลใหม่รวมถึง 'ลูก ๆ ของมันทั้งหมด (ตัวอย่างที่ 1) ในตัวอย่างที่ 2 ความแตกต่างจะประสบความสำเร็จjob-titleและunemployment-reasonเนื่องจากตอนนี้มีคีย์ที่แตกต่างกัน

แน่นอนคุณสามารถใช้คีย์ใดก็ได้ที่คุณต้องการตราบเท่าที่ไม่ซ้ำกัน


อัปเดตเดือนสิงหาคม 2560

สำหรับความเข้าใจที่ดีขึ้นในวิธีการทำงานในปุ่มตอบสนองผมขอแนะนำให้อ่านคำตอบของฉันคีย์ที่ไม่ซ้ำกันในการทำความเข้าใจ React.js


อัปเดตเดือนพฤศจิกายน 2560

การอัปเดตนี้ควรได้รับการโพสต์เมื่อไม่นานมานี้ แต่การใช้ตัวอักษรสตริงในrefตอนนี้เลิกใช้แล้ว ตัวอย่างเช่นref="job-title"ตอนนี้ควรเป็นref={(el) => this.jobTitleRef = el}(ตัวอย่าง) ดูคำตอบของฉันสำหรับคำเตือนการเลิกใช้งานโดยใช้ this.refsสำหรับข้อมูลเพิ่มเติม


10
it will determine which components are new and which are old based on their key property.- นั่นคือ ... กุญแจ ... สู่ความเข้าใจของฉัน
Larry

1
ขอบคุณมาก ! ฉันยังพบว่าส่วนประกอบลูกทั้งหมดจากแผนที่ได้รับการเรนเดอร์ใหม่สำเร็จและฉันไม่เข้าใจว่าทำไม
ValentinVoilean

คำใบ้ที่ดีคือการใช้ตัวแปรสถานะ (ซึ่งเปลี่ยนแปลงเช่น id สำหรับเช่น) เป็นกุญแจสำคัญสำหรับ div!
Macilias

203

เปลี่ยนคีย์ของส่วนประกอบ

<Component key="1" />
<Component key="2" />

คอมโพเนนต์จะถูกยกเลิกการต่อเชื่อมและอินสแตนซ์ใหม่ของคอมโพเนนต์จะถูกต่อเชื่อมเนื่องจากคีย์มีการเปลี่ยนแปลง

แก้ไข : เอกสารเกี่ยวกับคุณอาจไม่ต้องการสถานะที่ได้รับ :

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


46
นี่ควรเป็นวิธีที่ชัดเจนกว่าในเอกสาร React สิ่งนี้บรรลุสิ่งที่ฉันเป็น แต่ใช้เวลาหลายชั่วโมงในการค้นหา
พอล

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

@ อเล็กซ์พี. ดี! บางครั้งภาพเคลื่อนไหว CSS อาจเป็นเรื่องยุ่งยาก ฉันสนใจที่จะดูตัวอย่าง
Alex K

1
เอกสารการตอบสนองที่อธิบายถึงเทคนิคนี้: reactjs.org/blog/2018/06/07/…
GameSalutes

ขอบคุณ. ฉันรู้สึกขอบคุณมากสำหรับเรื่องนี้ ฉันลองป้อนตัวเลขสุ่มให้กับอุปกรณ์ประกอบฉากที่ไม่ได้ประกาศเช่น "randomNumber" แต่เสาเดียวที่ใช้งานได้คือกุญแจสำคัญ
ShameWare

5

ใช้setStateในมุมมองของคุณเพื่อเปลี่ยนemployedคุณสมบัติของรัฐ นี่คือตัวอย่างของ React render engine

 someFunctionWhichChangeParamEmployed(isEmployed) {
      this.setState({
          employed: isEmployed
      });
 }

 getInitialState() {
      return {
          employed: true
      }
 },

 render(){
    if (this.state.employed) {
        return (
            <div>
                <MyInput ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div>
                <span>Diff me!</span>
                <MyInput ref="unemployment-reason" name="unemployment-reason" />
                <MyInput ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

ฉันทำอยู่แล้ว ตัวแปร 'ลูกจ้าง' เป็นส่วนหนึ่งของสถานะส่วนประกอบมุมมองและจะเปลี่ยนผ่านฟังก์ชัน setState ขออภัยที่ไม่ได้อธิบายเรื่องนั้น
01

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

คุณจะเปลี่ยน this.state.employed ได้อย่างไร? กรุณาแสดงรหัส คุณแน่ใจหรือไม่ว่าคุณใช้ setState ในตำแหน่งที่เหมาะสมในโค้ดของคุณ?
Dmitriy

คอมโพเนนต์มุมมองซับซ้อนกว่าที่ตัวอย่างของฉันแสดงเล็กน้อย นอกจากส่วนประกอบ MyInput แล้วยังแสดงผลกลุ่ม radiobutton ซึ่งควบคุมค่าของ 'ลูกจ้าง' ด้วยตัวจัดการ onChange ในตัวจัดการ onChange ฉันตั้งค่าสถานะขององค์ประกอบมุมมองตามนั้น มุมมองจะแสดงผลใหม่ได้ดีเมื่อค่าของกลุ่ม radiobutton เปลี่ยนไป แต่ React คิดว่า MyInput-component ตัวแรกจะเหมือนกับก่อนที่ "ใช้งาน" เปลี่ยนไป
o01

0

ฉันกำลังทำงานกับ Crud สำหรับแอปของฉัน นี่คือวิธีที่ฉันได้รับ Reactstrap เป็นที่พึ่งพาของฉัน

import React, { useState, setState } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import firebase from 'firebase';
// import { LifeCrud } from '../CRUD/Crud';
import { Row, Card, Col, Button } from 'reactstrap';
import InsuranceActionInput from '../CRUD/InsuranceActionInput';

const LifeActionCreate = () => {
  let [newLifeActionLabel, setNewLifeActionLabel] = React.useState();

  const onCreate = e => {
    const db = firebase.firestore();

    db.collection('actions').add({
      label: newLifeActionLabel
    });
    alert('New Life Insurance Added');
    setNewLifeActionLabel('');
  };

  return (
    <Card style={{ padding: '15px' }}>
      <form onSubmit={onCreate}>
        <label>Name</label>
        <input
          value={newLifeActionLabel}
          onChange={e => {
            setNewLifeActionLabel(e.target.value);
          }}
          placeholder={'Name'}
        />

        <Button onClick={onCreate}>Create</Button>
      </form>
    </Card>
  );
};

React Hooks อยู่ในนั้น


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