جافاسكريبت - كيف يمكنني تحديث حالة العنصر الأب في React؟
تبدو بنيتي كما يلي:
Component 1
- |- Component 2
- - |- Component 4
- - - |- Component 5
Component 3
يجب أن يعرض المكون 3 بعض البيانات اعتمادًا على حالة المكون 5.
نظرًا لأن الخصائص غير قابلة للتغيير، لا يمكنني ببساطة حفظ حالتها في المكوّن 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}/ >
}
}
بهذه الطريقة يمكن للطفل تحديث حالة الوالد من خلال استدعاء دالة تم تمريرها عبر الخصائص.
لكن سيتعين عليك إعادة التفكير في هيكل مكوناتك، لأنه حسب ما أفهم فإن المكونين 5 و3 ليسا مرتبطين.
أحد الحلول الممكنة هو تغليفهم في مكون أعلى مستوى يحتوي على حالة كل من المكون 1 والمكون 3. هذا المكون سيضبط الحالة في المستوى الأدنى من خلال الخصائص.
الإجابة رقم 2
هذه هي الطريقة للقيام بذلك باستخدام الخطاف الجديد useState.
الطريقة - مرر دالة تغيير الحالة كخاصية إلى المكون الفرعي وافعل ما تريد بالدالة:
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 بتحديث حالته، مما يؤدي إلى إعادة التصيير (re-render). هذا يتطلب مكونات حالة (stateful components)، والتي، حسب وجهة نظرك، قد تكون نمطًا معاديًا أو لا. شخصيًا أنا ضدها وأفضل أن يكون هناك شيء آخر يستمع للإرسالات ويغير الحالة من الأعلى إلى الأسفل (كما يفعل 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>
}
موحد الإرسال مع Flux بسيط جدًا. فهو ببساطة يسجل ردود الاتصال ويستدعيها عند حدوث أي إرسال، مارًّا بالمحتويات على الإرسال (في المثال المختصر أعلاه لا يوجد payload مع الإرسال، مجرد معرف رسالة). يمكنك تكييف هذا مع نموذج النشر/الاشتراك التقليدي (على سبيل المثال، باستخدام 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>
)
} }
