วิธีที่ถูกต้องในการผ่านสถานะองค์ประกอบของแบบฟอร์มไปยังองค์ประกอบพี่น้อง / ผู้ปกครองคืออะไร?


186
  • สมมติว่าฉันมีคลาส React P ซึ่งแสดงคลาสลูกสองคลาสคือ C1 และ C2
  • C1 มีฟิลด์อินพุต ฉันจะอ้างถึงฟิลด์อินพุตนี้เป็น Foo
  • เป้าหมายของฉันคือให้ C2 ตอบสนองต่อการเปลี่ยนแปลงใน Foo

ฉันคิดวิธีแก้ปัญหาสองข้อ แต่ก็ไม่ได้รู้สึกถูก

วิธีแก้ปัญหาแรก:

  1. กำหนด P state.inputรัฐ
  2. สร้างonChangeฟังก์ชั่นในโหมด P state.inputซึ่งใช้เวลาในเหตุการณ์และชุด
  3. ส่งสิ่งนี้onChangeไปที่ C1 เป็น a propsและให้ C1 ผูกthis.props.onChangeกับonChangeFoo

วิธีนี้ใช้ได้ผล เมื่อใดก็ตามที่ค่าของ Foo เปลี่ยนแปลงจะมีการเรียก a setStateใน P ดังนั้น P จะมีอินพุตที่จะผ่านไปยัง C2

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

ทางออกที่สอง:

เพียงแค่ใส่ Foo ใน P

แต่นี่เป็นหลักการออกแบบที่ฉันควรปฏิบัติตามเมื่อฉันจัดโครงสร้างแอปของฉัน - ใส่องค์ประกอบแบบฟอร์มทั้งหมดในrenderระดับสูงสุดหรือไม่

เช่นในตัวอย่างของฉันถ้าฉันมีการเรนเดอร์ขนาดใหญ่ของ C1 ฉันไม่ต้องการใส่ทั้งrenderC1 ถึงrenderของ P เพียงเพราะ C1 มีองค์ประกอบแบบฟอร์ม

ฉันควรทำอย่างไร


ฉันกำลังจะทำสิ่งเดียวกันและแม้ว่ามันจะทำงานอย่างถูกต้องฉันมีความรู้สึกว่ามันเป็นเพียงแค่แฮ็คยักษ์
Mirko

คำตอบ:


198

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

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

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

class Example extends React.Component {
  constructor (props) {
    super(props)
    this.state = { data: 'test' }
  }
  render () {
    return (
      <div>
        <C1 onUpdate={this.onUpdate.bind(this)}/>
        <C2 data={this.state.data}/>
      </div>
    )
  }
  onUpdate (data) { this.setState({ data }) }
}

class C1 extends React.Component {
    render () {
      return (
        <div>
          <input type='text' ref='myInput'/>
          <input type='button' onClick={this.update.bind(this)} value='Update C2'/>
        </div>
      )
    }
    update () {
      this.props.onUpdate(this.refs.myInput.getDOMNode().value)
    }
})

class C2 extends React.Component {
    render () {
      return <div>{this.props.data}</div>
    }
})

ReactDOM.renderComponent(<Example/>, document.body)

5
ไม่มีปัญหา. ฉันกลับไปหลังจากเขียนบทความนี้แล้วอ่านเอกสารอีกครั้งและดูเหมือนว่าสอดคล้องกับความคิดและแนวปฏิบัติที่ดีที่สุดของพวกเขา React มีเอกสารที่ยอดเยี่ยมอย่างแท้จริงและทุกครั้งที่ฉันจบลงด้วยการสงสัยว่าจะต้องมีอะไรไปบ้างพวกเขามักจะมีเอกสารอยู่ในเอกสาร ตรวจสอบส่วนที่เกี่ยวกับสถานะที่นี่facebook.github.io/react/docs/…
captray

@captray แต่สิ่งที่เกี่ยวกับถ้าC2มีgetInitialStateสำหรับข้อมูลและภายในrenderใช้this.state.data?
Dmitry Polushkin

2
@DmitryPolushkin หากฉันเข้าใจคำถามของคุณถูกต้องคุณต้องการส่งผ่านข้อมูลจาก Root Component ของคุณไปที่ C2 เป็นอุปกรณ์ประกอบฉาก ใน C2 ข้อมูลนั้นจะถูกตั้งค่าเป็นสถานะเริ่มต้น (เช่น getInitialState: function () {return {someData: this.props.dataFromParentThatWillChange}} และคุณจะต้องติดตั้ง componentWillReceiveProps และเรียก props นี้เพื่อตั้งค่าใหม่ state ใน C2 ตั้งแต่แรกที่ฉันตอบฉันใช้ Flux และขอแนะนำให้คุณดูด้วยเช่นกันมันทำให้ส่วนประกอบของคุณสะอาดขึ้นและจะเปลี่ยนวิธีที่คุณคิดเกี่ยวกับสถานะ
captray

3
@DmitryPolushkin ฉันต้องการโพสต์สิ่งนี้เพื่อติดตาม facebook.github.io/react/tips/…มันโอเคตราบใดที่คุณรู้ว่าคุณกำลังทำอะไรและคุณรู้ว่าข้อมูลกำลังจะเปลี่ยนแปลง แต่ในหลาย ๆ สถานการณ์คุณสามารถย้ายสิ่งต่าง ๆ ได้ สิ่งสำคัญคือต้องทราบว่าคุณไม่จำเป็นต้องสร้างสิ่งนี้เป็นลำดับชั้น คุณสามารถเมานต์ C1 และ C2 ในสถานที่ต่าง ๆ ใน DOM และพวกเขาสามารถฟังการเปลี่ยนแปลงเหตุการณ์ในข้อมูลบางอย่าง ฉันเห็นผู้คนจำนวนมากผลักดันให้มีองค์ประกอบแบบลำดับชั้นเมื่อพวกเขาไม่ต้องการพวกเขา
อธิบายภาพ

5
รหัสด้านบนมีข้อบกพร่อง 2 ข้อซึ่งทั้งสองอย่างนี้ไม่เกี่ยวข้องกับ "นี่" ในบริบทที่ถูกต้องฉันได้ทำการแก้ไขด้านบนและสำหรับผู้ที่ต้องการสาธิตโคเดเพน
Alex H

34

เมื่อใช้ React เพื่อสร้างแอปตอนนี้ฉันอยากจะแบ่งปันความคิดกับคำถามนี้ที่ฉันถามเมื่อครึ่งปีที่แล้ว

ฉันแนะนำให้คุณอ่าน

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

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

กลับไปที่คำถาม

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

อย่างไรก็ตามการปล่อยให้ผู้จัดการทริกเกอร์ setState ใน P สามารถถูกหรือผิดขึ้นอยู่กับสถานการณ์ของคุณ

หากแอปเป็นตัวแปลง Markdown แบบง่าย C1 เป็นอินพุตดิบและ C2 เป็นเอาต์พุต HTML ก็โอเคที่จะให้ C1 ทริกเกอร์ setState ใน P แต่บางคนอาจโต้แย้งว่านี่ไม่ใช่วิธีที่แนะนำให้ทำ

แต่ถ้าแอปคือรายการสิ่งที่ต้องทำ, C1 ถูกป้อนข้อมูลสำหรับการสร้างสิ่งที่ต้องทำใหม่ C2 รายการสิ่งที่ต้องทำใน HTML, คุณอาจต้องการที่จะจัดการไปสองระดับขึ้นกว่า P - เพื่อdispatcherที่ให้storeการปรับปรุงdata store, ซึ่งส่งข้อมูลไปยัง P แล้วเติมมุมมอง ดูที่บทความ Flux นี่คือตัวอย่าง: Flux - TodoMVC

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


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

@captray เราสามารถพูดได้ไหมว่าการ Redux นั้นทรงพลังกว่าปฏิกิริยาในทุกสถานการณ์? แม้จะมีช่วงการเรียนรู้ของพวกเขา ... (มาจาก Java)
บรูซซัน

ฉันไม่แน่ใจว่าคุณหมายถึงอะไร พวกเขาจัดการสองสิ่งที่แตกต่างกันเป็นอย่างดีและเป็นการแยกความกังวลที่เหมาะสม
บรรยายภาพ

1
เรามีโครงการที่ใช้ redux และใช้เวลาหลายเดือนดูเหมือนว่าจะใช้การกระทำทุกอย่าง มันเหมือนกับปาเก็ตตี้โค้ดขนาดใหญ่ที่ผสมผสานกันนี้และเป็นไปไม่ได้ที่จะเข้าใจภัยพิบัติทั้งหมด การจัดการของรัฐอาจจะดี แต่อาจเข้าใจผิดอย่างรุนแรง จะปลอดภัยหรือไม่หากสมมติว่าควรใช้ฟลักซ์ / การเปลี่ยนค่าสำหรับส่วนต่าง ๆ ของรัฐที่จำเป็นต้องเข้าถึงทั่วโลกเท่านั้น?
wayofthefuture

1
@wayofthefuture หากไม่เห็นรหัสของคุณฉันสามารถพูดได้ว่าฉันเห็นสปาเก็ตตี้มากมายตอบสนองเหมือน ole spaghetti jQuery ที่ดี คำแนะนำที่ดีที่สุดที่ฉันมีให้คือลองทำตาม SRP ทำให้ส่วนประกอบของคุณง่ายที่สุดเท่าที่จะทำได้ องค์ประกอบการแสดงผลเป็นใบ้ถ้าคุณสามารถ ฉันยังผลักดันให้ abstractions เหมือนส่วน <DataProvider /> มันทำสิ่งหนึ่งได้ดี ให้ข้อมูล ซึ่งมักจะกลายเป็นองค์ประกอบ 'รูท' และส่งผ่านข้อมูลโดยใช้ประโยชน์จากเด็ก ๆ (และการโคลนนิ่ง) เป็นอุปกรณ์ประกอบฉาก (สัญญาที่กำหนดไว้) ในที่สุดลองคิดบนเซิร์ฟเวอร์ก่อน มันจะทำให้ JS ของคุณสะอาดยิ่งขึ้นด้วยโครงสร้างข้อมูลที่ดีขึ้น
อธิบายภาพ

6

ห้าปีต่อมาด้วยการแนะนำของ React Hooks ตอนนี้มีวิธีที่สง่างามมากขึ้นในการใช้ตะขอ useContext

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

import React, { useState, useContext } from "react";
import ReactDOM from "react-dom";
import styles from "./styles.css";

// Create context container in a global scope so it can be visible by every component
const ContextContainer = React.createContext(null);

const initialAppState = {
  selected: "Nothing"
};

function App() {
  // The app has a state variable and update handler
  const [appState, updateAppState] = useState(initialAppState);

  return (
    <div>
      <h1>Passing state between components</h1>

      {/* 
          This is a context provider. We wrap in it any children that might want to access
          App's variables.
          In 'value' you can pass as many objects, functions as you want. 
           We wanna share appState and its handler with child components,           
       */}
      <ContextContainer.Provider value={{ appState, updateAppState }}>
        {/* Here we load some child components */}
        <Book title="GoT" price="10" />
        <DebugNotice />
      </ContextContainer.Provider>
    </div>
  );
}

// Child component Book
function Book(props) {
  // Inside the child component you can import whatever the context provider allows.
  // Earlier we passed value={{ appState, updateAppState }}
  // In this child we need the appState and the update handler
  const { appState, updateAppState } = useContext(ContextContainer);

  function handleCommentChange(e) {
    //Here on button click we call updateAppState as we would normally do in the App
    // It adds/updates comment property with input value to the appState
    updateAppState({ ...appState, comment: e.target.value });
  }

  return (
    <div className="book">
      <h2>{props.title}</h2>
      <p>${props.price}</p>
      <input
        type="text"
        //Controlled Component. Value is reverse vound the value of the variable in state
        value={appState.comment}
        onChange={handleCommentChange}
      />
      <br />
      <button
        type="button"
        // Here on button click we call updateAppState as we would normally do in the app
        onClick={() => updateAppState({ ...appState, selected: props.title })}
      >
        Select This Book
      </button>
    </div>
  );
}

// Just another child component
function DebugNotice() {
  // Inside the child component you can import whatever the context provider allows.
  // Earlier we passed value={{ appState, updateAppState }}
  // but in this child we only need the appState to display its value
  const { appState } = useContext(ContextContainer);

  /* Here we pretty print the current state of the appState  */
  return (
    <div className="state">
      <h2>appState</h2>
      <pre>{JSON.stringify(appState, null, 2)}</pre>
    </div>
  );
}

const rootElement = document.body;
ReactDOM.render(<App />, rootElement);

คุณสามารถเรียกใช้ตัวอย่างนี้ในตัวแก้ไขรหัส Sandbox

แก้ไข pass-state-with-context


5

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


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

2

ฉันประหลาดใจที่ไม่มีคำตอบด้วยวิธีการแก้ปัญหาปฏิกิริยาที่ตรงไปตรงมาในขณะนี้ ดังนั้นนี่คือหนึ่ง (เปรียบเทียบขนาดและความซับซ้อนกับผู้อื่น):

class P extends React.Component {
    state = { foo : "" };

    render(){
        const { foo } = this.state;

        return (
            <div>
                <C1 value={ foo } onChange={ x => this.setState({ foo : x })} />
                <C2 value={ foo } />
            </div>
        )
    }
}

const C1 = ({ value, onChange }) => (
    <input type="text"
           value={ value }
           onChange={ e => onChange( e.target.value ) } />
);

const C2 = ({ value }) => (
    <div>Reacting on value change: { value }</div>
);

ฉันกำลังตั้งค่าสถานะขององค์ประกอบหลักจากองค์ประกอบย่อย สิ่งนี้ดูเหมือนจะหักหลังหลักการออกแบบของ React: การไหลของข้อมูลทิศทางเดียว

ใด ๆ ที่มีการควบคุมinput (วิธีการทำงานแบบมีสำนวนใน React) จะอัพเดทสถานะผู้ปกครองในการonChangeติดต่อกลับและยังคงไม่หักหลังอะไรเลย

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


1
คุณใช้ปฏิกิริยารุ่นใด ฉันได้รับปัญหาคุณสมบัติของคลาสทดสอบและไม่ได้กำหนด foo
Isaac Pak

นั่นไม่เกี่ยวกับปฏิกิริยารุ่นหนึ่ง รหัสขององค์ประกอบผิด แก้ไขให้ลองตอนนี้
gaperton

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

1
ฉันชอบวิธีนี้ หากใครต้องการคนจรจัดกับมันนี่เป็นกล่องทรายสำหรับคุณ
Isaac Pak

2

คำตอบล่าสุดพร้อมตัวอย่างซึ่งใช้ React.useState

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

เมื่อรัฐอยู่ในองค์ประกอบผู้ปกครองเด็กสามารถกลายพันธุ์ได้ถ้าผู้ปกครองให้เด็กvalueและonChangeผู้จัดการในอุปกรณ์ประกอบฉาก (บางครั้งมันเรียกว่าการเชื่อมโยงค่าหรือรูปแบบการเชื่อมโยงรัฐ ) นี่คือวิธีที่คุณจะทำกับ hooks:


function Parent() {
    var [state, setState] = React.useState('initial input value');
    return <>
        <Child1 value={state} onChange={(v) => setState(v)} />
        <Child2 value={state}>
    </>
}

function Child1(props) {
    return <input
        value={props.value}
        onChange={e => props.onChange(e.target.value)}
    />
}

function Child2(props) {
    return <p>Content of the state {props.value}</p>
}

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

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

นี่คือลักษณะของ Hookstate Child1จะเปลี่ยนอินพุตChild2จะตอบสนองต่อมัน Parentจะถือสถานะ แต่จะไม่แสดงผลอีกครั้งเมื่อมีการเปลี่ยนแปลงสถานะเท่านั้นChild1และChild2จะ

import { useStateLink } from '@hookstate/core';

function Parent() {
    var state = useStateLink('initial input value');
    return <>
        <Child1 state={state} />
        <Child2 state={state}>
    </>
}

function Child1(props) {
    // to avoid parent re-render use local state,
    // could use `props.state` instead of `state` below instead
    var state = useStateLink(props.state)
    return <input
        value={state.get()}
        onChange={e => state.set(e.target.value)}
    />
}

function Child2(props) {
    // to avoid parent re-render use local state,
    // could use `props.state` instead of `state` below instead
    var state = useStateLink(props.state)
    return <p>Content of the state {state.get()}</p>
}

PS: มีตัวอย่างอีกมากมายที่นี่ซึ่งครอบคลุมสถานการณ์ที่คล้ายกันและมีความซับซ้อนมากขึ้นรวมถึงข้อมูลที่ซ้อนกันอย่างลึกล้ำการตรวจสอบสถานะรัฐทั่วโลกพร้อมsetStateเบ็ดเป็นต้นนอกจากนี้ยังมีตัวอย่างแอปพลิเคชันออนไลน์แบบสมบูรณ์ซึ่งใช้ Hookstate และเทคนิคที่อธิบายไว้ข้างต้น


1

คุณควรเรียนรู้ Redux และ ReactRedux library มันจะจัดโครงสร้างสถานะและอุปกรณ์ประกอบฉากของคุณในร้านเดียวและคุณสามารถเข้าถึงได้ในส่วนประกอบของคุณในภายหลัง


1

ด้วย React> = 16.3 คุณสามารถใช้ ref และ forwardRef เพื่อเข้าถึง DOM ของเด็ก ๆ จากพาเรนต์ อย่าใช้วิธีการอ้างอิงแบบเดิมอีกต่อไป
นี่คือตัวอย่างการใช้กรณีของคุณ:

import React, { Component } from 'react';

export default class P extends React.Component {
   constructor (props) {
      super(props)
      this.state = {data: 'test' }
      this.onUpdate = this.onUpdate.bind(this)
      this.ref = React.createRef();
   }

   onUpdate(data) {
      this.setState({data : this.ref.current.value}) 
   }

   render () {
      return (
        <div>
           <C1 ref={this.ref} onUpdate={this.onUpdate}/>
           <C2 data={this.state.data}/>
        </div>
      )
   }
}

const C1 = React.forwardRef((props, ref) => (
    <div>
        <input type='text' ref={ref} onChange={props.onUpdate} />
    </div>
));

class C2 extends React.Component {
    render () {
       return <div>C2 reacts : {this.props.data}</div>
    }
}

ดูRefsและForwardRefสำหรับข้อมูลโดยละเอียดเกี่ยวกับ refs และ forwardRef


0
  1. สิ่งที่ถูกต้องคือการมีสถานะในองค์ประกอบพาเรนต์เพื่อหลีกเลี่ยงการอ้างอิงและสิ่งที่ไม่
  2. ปัญหาคือการหลีกเลี่ยงการปรับปรุงเด็กทุกคนเมื่อพิมพ์ลงในช่อง
  3. ดังนั้นเด็กแต่ละคนควรเป็นส่วนประกอบ (ไม่ใช่ใน PureComponent) และนำไปใช้ shouldComponentUpdate(nextProps, nextState)
  4. วิธีนี้เมื่อพิมพ์ลงในเขตข้อมูลแบบฟอร์มเฉพาะเขตข้อมูลที่ปรับปรุง

รหัสด้านล่างใช้@boundคำอธิบายประกอบจากES.Next babel-plugin-transform-decorators-legacyของBabelJS 6และคุณสมบัติคลาส (การเพิ่มความคิดเห็นจะตั้งค่านี้ในฟังก์ชั่นสมาชิกที่คล้ายกับการผูก):

/*
© 2017-present Harald Rudell <harald.rudell@gmail.com> (http://www.haraldrudell.com)
All rights reserved.
*/
import React, {Component} from 'react'
import {bound} from 'class-bind'

const m = 'Form'

export default class Parent extends Component {
  state = {one: 'One', two: 'Two'}

  @bound submit(e) {
    e.preventDefault()
    const values = {...this.state}
    console.log(`${m}.submit:`, values)
  }

  @bound fieldUpdate({name, value}) {
    this.setState({[name]: value})
  }

  render() {
    console.log(`${m}.render`)
    const {state, fieldUpdate, submit} = this
    const p = {fieldUpdate}
    return (
      <form onSubmit={submit}> {/* loop removed for clarity */}
        <Child name='one' value={state.one} {...p} />
        <Child name='two' value={state.two} {...p} />
        <input type="submit" />
      </form>
    )
  }
}

class Child extends Component {
  value = this.props.value

  @bound update(e) {
    const {value} = e.target
    const {name, fieldUpdate} = this.props
    fieldUpdate({name, value})
  }

  shouldComponentUpdate(nextProps) {
    const {value} = nextProps
    const doRender = value !== this.value
    if (doRender) this.value = value
    return doRender
  }

  render() {
    console.log(`Child${this.props.name}.render`)
    const {value} = this.props
    const p = {value}
    return <input {...p} onChange={this.update} />
  }
}

0

แนวคิดของการส่งผ่านข้อมูลจากผู้ปกครองไปยังเด็กและในทางกลับกันมีการอธิบาย

import React, { Component } from "react";
import ReactDOM from "react-dom";

// taken refrence from https://gist.github.com/sebkouba/a5ac75153ef8d8827b98

//example to show how to send value between parent and child

//  props is the data which is passed to the child component from the parent component

class Parent extends Component {
  constructor(props) {
    super(props);

    this.state = {
      fieldVal: ""
    };
  }

  onUpdateParent = val => {
    this.setState({
      fieldVal: val
    });
  };

  render() {
    return (
      // To achieve the child-parent communication, we can send a function
      // as a Prop to the child component. This function should do whatever
      // it needs to in the component e.g change the state of some property.
      //we are passing the function onUpdateParent to the child
      <div>
        <h2>Parent</h2>
        Value in Parent Component State: {this.state.fieldVal}
        <br />
        <Child onUpdate={this.onUpdateParent} />
        <br />
        <OtherChild passedVal={this.state.fieldVal} />
      </div>
    );
  }
}

class Child extends Component {
  constructor(props) {
    super(props);

    this.state = {
      fieldValChild: ""
    };
  }

  updateValues = e => {
    console.log(e.target.value);
    this.props.onUpdate(e.target.value);
    // onUpdateParent would be passed here and would result
    // into onUpdateParent(e.target.value) as it will replace this.props.onUpdate
    //with itself.
    this.setState({ fieldValChild: e.target.value });
  };

  render() {
    return (
      <div>
        <h4>Child</h4>
        <input
          type="text"
          placeholder="type here"
          onChange={this.updateValues}
          value={this.state.fieldVal}
        />
      </div>
    );
  }
}

class OtherChild extends Component {
  render() {
    return (
      <div>
        <h4>OtherChild</h4>
        Value in OtherChild Props: {this.props.passedVal}
        <h5>
          the child can directly get the passed value from parent by this.props{" "}
        </h5>
      </div>
    );
  }
}

ReactDOM.render(<Parent />, document.getElementById("root"));


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

@ThomasEaso ฉันไม่พบเขาที่นี่ฉันตรวจสอบแล้ว มีข้อเสนอแนะอื่น ๆ อีก
ไหม

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