ค้นหาเว็บไซต์

javascript - ฉันจะอัปเดตสถานะของพ่อแม่ใน React ได้อย่างไร?


โครงสร้างของฉันมีลักษณะดังนี้:

Component 1

 - |- Component 2


 - - |- Component 4


 - - -  |- Component 5

Component 3

ส่วนประกอบ 3 ควรแสดงข้อมูลบางอย่างขึ้นอยู่กับสถานะของส่วนประกอบ 5

เนื่องจาก props เป็นค่าที่ไม่สามารถเปลี่ยนแปลงได้ ฉันจึงไม่สามารถบันทึกสถานะของมันใน Component 1 และส่งต่อได้ใช่ไหม? และใช่ ฉันได้อ่านเกี่ยวกับ Redux แต่ฉันไม่อยากใช้มัน ฉันหวังว่าสามารถแก้ไขได้เพียงแค่ใช้ React ใช่ไหมที่ฉันคิดผิด?

คำตอบ #1

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

class Parent extends React.Component {
  constructor(props) {
    super(props)

    this.handler = this.handler.bind(this)
  }

  handler() {
    this.setState({
      someVar: 'some value'
    })
  }

  render() {
    return <Child handler = {this.handler} />
  }
}

class Child extends React.Component {
  render() {
    return <Button onClick = {this.props.handler}/ >
  }
}

ด้วยวิธีนี้ เด็กสามารถอัปเดตสถานะของพ่อแม่ได้ด้วยการเรียกฟังก์ชันที่ถูกส่งมาพร้อมกับ props

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

วิธีแก้ปัญหาอย่างหนึ่งคือการห่อหุ้มพวกมันในคอมโพเนนต์ระดับสูงซึ่งจะเก็บสถานะของทั้งคอมโพเนนต์ 1 และ 3 คอมโพเนนต์นี้จะตั้งค่าสถานะระดับล่างผ่าน props

คำตอบ #2

นี่คือวิธีทำด้วย hook useState ตัวใหม่

วิธีการ - ส่งฟังก์ชันเปลี่ยนสถานะเป็น props ไปยังคอมโพเนนต์ลูกและทำอะไรก็ได้กับฟังก์ชันนั้น:

import React, {useState} from 'react';

const ParentComponent = () => {
  const[state, setState]=useState('');
  
  return(
    <ChildComponent stateChanger={setState} />
  )
}


const ChildComponent = ({stateChanger, ...rest}) => {
  return(
    <button onClick={() => stateChanger('New data')}></button>
  )
}

คำตอบ #3

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

เวอร์ชันที่มีการส่งเมธอด()

//ChildB component
class ChildB extends React.Component {

    render() {

        var handleToUpdate = this.props.handleToUpdate;
        return (<div><button onClick={() => handleToUpdate('someVar')}>
            Push me
          </button>
        </div>)
    }
}

//ParentA component
class ParentA extends React.Component {

    constructor(props) {
        super(props);
        var handleToUpdate = this.handleToUpdate.bind(this);
        var arg1 = '';
    }

    handleToUpdate(someArg){
        alert('We pass argument from Child to Parent: ' + someArg);
        this.setState({arg1:someArg});
    }

    render() {
        var handleToUpdate = this.handleToUpdate;

        return (<div>
                    <ChildB handleToUpdate = {handleToUpdate.bind(this)} /></div>)
    }
}

if(document.querySelector("#demo")){
    ReactDOM.render(
        <ParentA />,
        document.querySelector("#demo")
    );
}

ดูที่ JSFiddle

เวอร์ชันที่มีการส่งฟังก์ชันลูกศร

//ChildB component
class ChildB extends React.Component {

    render() {

        var handleToUpdate = this.props.handleToUpdate;
        return (<div>
          <button onClick={() => handleToUpdate('someVar')}>
            Push me
          </button>
        </div>)
    }
}

//ParentA component
class ParentA extends React.Component {
    constructor(props) {
        super(props);
    }

    handleToUpdate = (someArg) => {
            alert('We pass argument from Child to Parent: ' + someArg);
    }

    render() {
        return (<div>
            <ChildB handleToUpdate = {this.handleToUpdate} /></div>)
    }
}

if(document.querySelector("#demo")){
    ReactDOM.render(
        <ParentA />,
        document.querySelector("#demo")
    );
}

ดูที่ JSFiddle

คำตอบ #4

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

 class Parent extends React.Component {
  constructor(props) {
    super(props)
    // without bind, replaced by arrow func below
  }

  handler = (val) => {
    this.setState({
      someVar: val
    })
  }

  render() {
    return <Child handler = {this.handler} />
  }
}

class Child extends React.Component {
  render() {
    return <Button onClick = {() => this.props.handler('the passing value')}/ >
  }
}

หวังว่ามันจะช่วยใครสักคนได้

คำตอบ #5

ฉันชอบคำตอบเกี่ยวกับการส่งฟังก์ชันไปรอบ ๆ มันเป็นเทคนิคที่สะดวกมาก

ในทางกลับกัน คุณก็สามารถทำสิ่งนี้ได้โดยใช้ pub/sub หรือตัวแปรหนึ่ง ตัวกระจายสัญญาณ (dispatcher) เหมือนที่ Flux ทำ ทฤษฎีง่ายมาก ให้คอมโพเนนต์ 5 ส่งข้อความซึ่งคอมโพเนนต์ 3 กำลังฟังอยู่ จากนั้นคอมโพเนนต์ 3 จะอัปเดตสถานะของมันซึ่งจะกระตุ้นการเรนเดอร์ใหม่ สิ่งนี้ต้องใช้คอมโพเนนต์ที่มีสถานะ ซึ่งขึ้นอยู่กับมุมมองของคุณ อาจถือว่าเป็นรูปแบบที่ไม่ดีหรือไม่ก็ได้ ส่วนตัวผมไม่เห็นด้วยกับการใช้คอมโพเนนต์แบบนี้และอยากให้มีบางสิ่งบางอย่างคอยฟังการส่งข้อความและเปลี่ยนสถานะจากบนลงล่าง (Redux ทำแบบนี้ แต่จะมีคำศัพท์เพิ่มเติมเข้ามา)

import { Dispatcher } from 'flux'
import { Component } from 'React'

const dispatcher = new Dispatcher()

// Component 3
// Some methods, such as constructor, omitted for brevity
class StatefulParent extends Component {
  state = {
    text: 'foo'
  } 

  componentDidMount() {
    dispatcher.register( dispatch => {
      if ( dispatch.type === 'change' ) {
        this.setState({ text: 'bar' })
      }
    }
  }

  render() {
    return <h1>{ this.state.text }</h1>
  }
}

// Click handler
const onClick = event => {
  dispatcher.dispatch({
    type: 'change'
  })
}

// Component 5 in your example
const StatelessChild = props => {
  return <button onClick={ onClick }>Click me</button> 
}

ตัวจัดส่ง (dispatcher) ที่มาพร้อมกับ Flux นั้นง่ายมาก มันเพียงแค่ลงทะเบียน callback และเรียกใช้ callback เหล่านั้นเมื่อมีการ dispatch เกิดขึ้น โดยส่งเนื้อหาที่อยู่ในการ dispatch ผ่านไป (ในตัวอย่างย่อด้านบนไม่มี payload กับการ dispatch มีเพียง id ของข้อความเท่านั้น) คุณสามารถปรับสิ่งนี้ให้เข้ากับ pub/sub แบบดั้งเดิม (เช่น ใช้ EventEmitter จาก events หรือเวอร์ชันอื่น ๆ) ได้อย่างง่ายดายถ้านั่นมีความหมายมากกว่าสำหรับคุณ

คำตอบ #6

ฉันพบวิธีแก้ปัญหาที่ใช้งานได้ดังต่อไปนี้ในการส่งอาร์กิวเมนต์ฟังก์ชัน onClick จากคอมโพเนนต์ลูกไปยังคอมโพเนนต์พ่อแม่พร้อมพารามิเตอร์:

คลาสหลัก:

class Parent extends React.Component {
constructor(props) {
    super(props)

    // Bind the this context to the handler function
    this.handler = this.handler.bind(this);

    // Set some state
    this.state = {
        messageShown: false
    };
}

// This method will be sent to the child component
handler(param1) {
console.log(param1);
    this.setState({
        messageShown: true
    });
}

// Render the child component and set the action property with the handler as value
render() {
    return <Child action={this.handler} />
}}

คลาสลูก:

class Child extends React.Component {
render() {
    return (
        <div>
            {/* The button will execute the handler function set by the parent component */}
            <Button onClick={this.props.action.bind(this,param1)} />
        </div>
    )
} }

บทความที่เกี่ยวข้อง