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>
)
} }
