Làm cách nào để cập nhật trạng thái của cha mẹ trong React?


349

Cấu trúc của tôi trông như sau:

Component 1  

 - |- Component 2


 - - |- Component 4


 - - -  |- Component 5  

Component 3

Thành phần 3 sẽ hiển thị một số dữ liệu tùy thuộc vào trạng thái của Thành phần 5. Vì các đạo cụ là bất biến, tôi không thể đơn giản lưu trạng thái của nó trong Thành phần 1 và chuyển tiếp nó, phải không? Và vâng, tôi đã đọc về redux, nhưng không muốn sử dụng nó. Tôi hy vọng rằng nó có thể giải quyết nó chỉ bằng phản ứng. Tôi có lầm không?


20
siêu dễ: truyền hàm cha-setState-Function qua thuộc tính cho thành phần con: <MyChildComponent setState = {p => {this.setState (p)}} /> Trong thành phần con gọi nó qua this.props. setState ({myObj, ...});
Marcel Ennix

@MarcelEnnix, Nhận xét của bạn tiết kiệm thời gian của tôi. Cảm ơn.
Dinith Minura

<MyChildComponent setState={(s,c)=>{this.setState(s, c)}} />nếu bạn định sử dụng bản hack này, hãy đảm bảo bạn hỗ trợ gọi lại.
Barkermn01

4
Vượt qua một cuộc gọi lại để thiết lập trạng thái của cha mẹ là một thực tế tồi tệ có thể dẫn đến các vấn đề bảo trì. Nó phá vỡ sự đóng gói và nó làm cho các thành phần 2 4 và 5 được liên kết chặt chẽ với 1. Nếu đi theo con đường này thì bạn sẽ không thể sử dụng lại bất kỳ thành phần con nào ở nơi khác. Tốt hơn là bạn có các đạo cụ cụ thể để các thành phần con có thể kích hoạt các sự kiện bất cứ khi nào có chuyện gì xảy ra, thì thành phần cha mẹ sẽ xử lý sự kiện đó đúng cách.
Pato Loco

@MarcelEnnix, tại sao dấu ngoặc nhọn xung quanh this.setState(p)? Tôi đã thử mà không có chúng và nó dường như hoạt động (Tôi rất mới với React)
Biganon

Câu trả lời:


677

Đối với giao tiếp giữa cha mẹ và con cái, bạn nên truyền một hàm thiết lập trạng thái từ cha mẹ sang con cái, như thế này

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

Bằng cách này, trẻ có thể cập nhật trạng thái của cha mẹ bằng lệnh gọi của hàm được truyền bằng đạo cụ.

Nhưng bạn sẽ phải suy nghĩ lại về cấu trúc các thành phần của mình, vì theo tôi hiểu thì các thành phần 5 và 3 không liên quan.

Một giải pháp khả thi là bọc chúng trong một thành phần cấp cao hơn sẽ chứa trạng thái của cả thành phần 1 và 3. Thành phần này sẽ đặt trạng thái cấp thấp hơn thông qua các đạo cụ.


6
tại sao bạn cần this.handler = this.handler.bind (this) mà không chỉ là hàm xử lý thiết lập trạng thái?
chemook78

34
@ chemook78 trong ES6 Các phương thức lớp React không tự động liên kết với lớp. Vì vậy, nếu chúng ta không thêm this.handler = this.handler.bind(this)vào hàm tạo, thisbên trong handlerhàm sẽ tham chiếu hàm đóng chứ không phải lớp. Nếu không muốn liên kết tất cả các hàm của bạn trong hàm tạo, có hai cách khác để xử lý việc này bằng các hàm mũi tên. Bạn chỉ có thể viết trình xử lý nhấp chuột onClick={()=> this.setState(...)}hoặc bạn có thể sử dụng trình khởi tạo thuộc tính cùng với các hàm mũi tên như được mô tả ở đây babeljs.io/blog/2015/06/07/react-on-es6-plus trong "Hàm mũi tên"
Ivan

1
Đây là một ví dụ về hành động này: plnkr.co/edit/tGWecotmktae8zjS5yEr?p=preview
Tamb

5
Đó là tất cả có ý nghĩa, tại sao e.preventDefault? và điều đó có cần jquery không?
Vincent Buscarello

1
Câu hỏi nhanh, điều này không cho phép sự phân công của một nhà nước địa phương trong đứa trẻ?
ThisGuyCantEven

50

Tôi đã tìm thấy giải pháp làm việc sau đây để truyền đối số hàm onClick từ con sang thành phần cha:

Phiên bản với việc truyền một phương thức ()

//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")
    );
}

Nhìn vào JSFIDDLE

Phiên bản có chức năng chuyển mũi tên

//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")
    );
}

Nhìn vào JSFIDDLE


Cái này tốt! bạn có thể giải thích phần này: <ChildB handleToUpdate = {handleToUpdate.bind(this)} />Tại sao phải ràng buộc một lần nữa?
Dane

@Dane - bạn phải ràng buộc bối cảnh của việc này để trở thành cha mẹ để khi thisđược gọi bên trong đứa trẻ, thisđề cập đến trạng thái của cha mẹ chứ không phải đứa trẻ. Đây là đóng cửa tốt nhất của nó!
Casey

@Casey nhưng chúng ta không làm điều đó trong hàm tạo? Và thế là chưa đủ ??
Dane

Bạn hoàn toàn đúng! Tôi đã bỏ lỡ nó. Có, nếu bạn đã làm điều đó trong hàm tạo rồi thì bạn đã ổn rồi!
Casey

Bạn là một người bạn đời huyền thoại! Điều này sẽ giữ cho các thành phần độc lập mà không bị buộc phải tạo các thành phần chính để xử lý các trao đổi trạng thái
adamj

13

Tôi muốn cảm ơn câu trả lời được đánh giá cao nhất vì đã cho tôi ý tưởng về vấn đề của riêng tôi về cơ bản là sự biến đổi của nó với chức năng mũi tên và chuyển param từ thành phần con:

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

Hy vọng nó sẽ giúp được ai đó.


Có gì đặc biệt về chức năng mũi tên qua cuộc gọi trực tiếp?
Ashish Kamble

@AshishKamble các hàm thistrong mũi tên đề cập đến bối cảnh của cha mẹ (tức là Parentlớp).
CPHPython

Đây là một câu trả lời trùng lặp. Bạn có thể thêm một nhận xét cho câu trả lời được chấp nhận và đề cập về tính năng thử nghiệm này để sử dụng chức năng mũi tên trong lớp.
Arashsoft

10

Tôi thích câu trả lời liên quan đến việc chuyển các chức năng xung quanh, đây là một kỹ thuật rất tiện dụng.

Mặt khác, bạn cũng có thể đạt được điều này bằng cách sử dụng pub / sub hoặc sử dụng một biến thể, một bộ điều phối, như Flux . Lý thuyết là siêu đơn giản, có thành phần 5 gửi một thông điệp mà thành phần 3 đang lắng nghe. Thành phần 3 sau đó cập nhật trạng thái kích hoạt kết xuất lại. Điều này đòi hỏi các thành phần trạng thái, tùy thuộc vào quan điểm của bạn, có thể hoặc không thể là một mô hình chống. Tôi chống lại cá nhân họ và thà rằng một cái gì đó khác đang lắng nghe công văn và thay đổi trạng thái từ trên xuống (Redux làm điều này, nhưng thêm thuật ngữ bổ sung).

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

Bộ điều phối gói với Flux rất đơn giản, nó chỉ đơn giản đăng ký các cuộc gọi lại và gọi chúng khi có bất kỳ công văn nào xảy ra, chuyển qua nội dung trên công văn (trong ví dụ ngắn gọn ở trên không payloadcó công văn, chỉ đơn giản là một id tin nhắn). Bạn có thể điều chỉnh điều này với pub / sub truyền thống (ví dụ: sử dụng EventEuctor từ các sự kiện hoặc một số phiên bản khác) rất dễ dàng nếu điều đó có ý nghĩa hơn với bạn.


Các thành phần Reacts của tôi đang "chạy" trong trình duyệt như trong một hướng dẫn chính thức ( facebook.github.io/react/docs/tutorial.html ) Tôi đã cố gắng đưa Flux với browserify, nhưng trình duyệt nói rằng không tìm thấy Dispatcher :(
wklm

2
Cú pháp tôi đã sử dụng là cú pháp mô-đun ES2016 yêu cầu dịch mã (tôi sử dụng Babel , nhưng có một số khác, biến đổi babelify có thể được sử dụng với browserify), nó chuyển sangvar Dispatcher = require( 'flux' ).Dispatcher
Matt Styles

8

Tôi đã tìm thấy giải pháp làm việc sau đây để truyền đối số chức năng onClick từ con sang thành phần cha mẹ với param:

lớp phụ huynh:

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

lớp trẻ em:

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

2
Bất cứ ai cũng có thể biết liệu đó có phải là một giải pháp chấp nhận được không (đặc biệt quan tâm đến việc truyền tham số như được đề xuất).
ilans

param1chỉ hiển thị trên bảng điều khiển không được gán mà nó luôn được gántrue
Ashish Kamble

Tôi không thể nói đến chất lượng của giải pháp, nhưng điều này thành công vượt qua thông số cho tôi.
Gia-cơ

6

Khi bạn yêu cầu giao tiếp giữa trẻ với cha mẹ ở mọi cấp độ, thì tốt hơn là sử dụng bối cảnh . Trong thành phần cha mẹ xác định bối cảnh có thể được gọi bởi đứa trẻ, chẳng hạn như

Trong thành phần cha mẹ trong trường hợp của bạn thành phần 3

static childContextTypes = {
        parentMethod: React.PropTypes.func.isRequired
      };

       getChildContext() {
        return {
          parentMethod: (parameter_from_child) => this.parentMethod(parameter_from_child)
        };
      }

parentMethod(parameter_from_child){
// update the state with parameter_from_child
}

Bây giờ trong thành phần con (thành phần 5 trong trường hợp của bạn), chỉ cần nói với thành phần này rằng nó muốn sử dụng bối cảnh của cha mẹ của nó.

 static contextTypes = {
       parentMethod: React.PropTypes.func.isRequired
     };
render(){
    return(
      <TouchableHighlight
        onPress={() =>this.context.parentMethod(new_state_value)}
         underlayColor='gray' >   

            <Text> update state in parent component </Text>              

      </TouchableHighlight>
)}

bạn có thể tìm thấy dự án Demo tại repo


Tôi không thể hiểu câu trả lời này, bạn có thể giải thích thêm về nó không
Ashish Kamble

5

Dường như chúng ta chỉ có thể truyền dữ liệu từ cha mẹ sang con vì phản ứng thúc đẩy Luồng dữ liệu một chiều, nhưng để làm cho cha mẹ tự cập nhật khi có gì đó xảy ra trong "thành phần con" của nó, chúng ta thường sử dụng cái gọi là "hàm gọi lại".

Chúng ta truyền hàm được định nghĩa trong cha mẹ cho con là "đạo cụ" và gọi hàm đó từ con kích hoạt nó trong thành phần cha.


class Parent extends React.Component {
  handler = (Value_Passed_From_SubChild) => {
    console.log("Parent got triggered when a grandchild button was clicked");
    console.log("Parent->Child->SubChild");
    console.log(Value_Passed_From_SubChild);
  }
  render() {
    return <Child handler = {this.handler} />
  }
}
class Child extends React.Component {
  render() {
    return <SubChild handler = {this.props.handler}/ >
  }
}
class SubChild extends React.Component { 
  constructor(props){
   super(props);
   this.state = {
      somethingImp : [1,2,3,4]
   }
  }
  render() {
     return <button onClick = {this.props.handler(this.state.somethingImp)}>Clickme<button/>
  }
}
React.render(<Parent />,document.getElementById('app'));

 HTML
 ----
 <div id="app"></div>

Trong ví dụ này, chúng ta có thể truyền dữ liệu từ SubChild -> Con -> Cha mẹ bằng cách truyền hàm cho Con trực tiếp của nó.


4

Tôi đã sử dụng một câu trả lời được xếp hạng hàng đầu từ trang này nhiều lần, nhưng trong khi học React, tôi đã tìm thấy một cách tốt hơn để làm điều đó, không ràng buộc và không có chức năng nội tuyến trong các đạo cụ.

Chỉ cần nhìn vào đây:

class Parent extends React.Component {

  constructor() {
    super();
    this.state={
      someVar: value
    }
  }

  handleChange=(someValue)=>{
    this.setState({someVar: someValue})
  }

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

}

export const Child = ({handler}) => {
  return <Button onClick={handler} />
}

Chìa khóa nằm trong chức năng mũi tên:

handleChange=(someValue)=>{
  this.setState({someVar: someValue})
}

Bạn có thể đọc thêm ở đây . Hy vọng điều này sẽ hữu ích cho ai đó =)


Điều này làm cho rất nhiều ý nghĩa với tôi. Cảm ơn bạn!
Brett Rowberry

3

-Chúng tôi có thể tạo ParentComponent và với phương thức handleInputChange để cập nhật trạng thái ParentComponent. Nhập ChildComponent và chúng tôi chuyển hai đạo cụ từ thành phần cha mẹ sang thành phần con eg.handleInputChange và đếm.

import React, { Component } from 'react';
import ChildComponent from './ChildComponent';

class ParentComponent extends Component {
  constructor(props) {
    super(props);
    this.handleInputChange = this.handleInputChange.bind(this);
    this.state = {
      count: '',
    };
  }

  handleInputChange(e) {
    const { value, name } = e.target;
    this.setState({ [name]: value });
  }

  render() {
    const { count } = this.state;
    return (
      <ChildComponent count={count} handleInputChange={this.handleInputChange} />
    );
  }
}
  • Bây giờ chúng tôi tạo tệp ChildComponent và lưu dưới dạng ChildComponent.jsx. Thành phần này không trạng thái vì thành phần con không có trạng thái. Chúng tôi sử dụng thư viện prop-type để kiểm tra loại đạo cụ.

    import React from 'react';
    import { func, number } from 'prop-types';
    
    const ChildComponent = ({ handleInputChange, count }) => (
      <input onChange={handleInputChange} value={count} name="count" />
    );
    
    ChildComponent.propTypes = {
      count: number,
      handleInputChange: func.isRequired,
    };
    
    ChildComponent.defaultProps = {
      count: 0,
    };
    
    export default ChildComponent;

Làm thế nào điều này hoạt động khi đứa trẻ có một đứa trẻ ảnh hưởng đến chỗ dựa của cha mẹ?
Bobort

3

Nếu kịch bản tương tự này không lan truyền khắp mọi nơi, bạn có thể sử dụng bối cảnh của React, đặc biệt nếu bạn không muốn giới thiệu tất cả các chi phí mà thư viện quản lý nhà nước giới thiệu. Thêm vào đó, nó dễ học hơn. Nhưng hãy cẩn thận, bạn có thể lạm dụng nó và bắt đầu viết mã xấu. Về cơ bản, bạn xác định một thành phần Container (sẽ giữ và giữ phần trạng thái đó cho bạn) làm cho tất cả các thành phần quan tâm đến việc viết / đọc đoạn dữ liệu đó là con của nó (không nhất thiết phải là con trực tiếp)

https://reactjs.org/docs/context.html

Thay vào đó, bạn cũng có thể sử dụng React đơn giản.

<Component5 onSomethingHappenedIn5={this.props.doSomethingAbout5} />

vượt qua doS Something About5 cho đến Hợp phần 1

    <Component1>
        <Component2 onSomethingHappenedIn5={somethingAbout5 => this.setState({somethingAbout5})}/>
        <Component5 propThatDependsOn5={this.state.somethingAbout5}/>
    <Component1/>

Nếu đây là một vấn đề phổ biến, bạn nên bắt đầu suy nghĩ chuyển toàn bộ trạng thái của ứng dụng sang một nơi khác. Bạn có một vài lựa chọn, phổ biến nhất là:

https://redux.js.org/

https://facebook.github.io/flux/

Về cơ bản, thay vì quản lý trạng thái ứng dụng trong thành phần của bạn, bạn gửi lệnh khi có điều gì đó xảy ra để cập nhật trạng thái. Các thành phần cũng kéo trạng thái từ thùng chứa này để tất cả dữ liệu được tập trung. Điều này không có nghĩa là không thể sử dụng trạng thái địa phương nữa, nhưng đó là một chủ đề nâng cao hơn.


2

vì vậy, nếu bạn muốn cập nhật thành phần cha mẹ,

 class ParentComponent extends React.Component {
        constructor(props){
            super(props);
            this.state = {
               page:0
            }
        }

        handler(val){
            console.log(val) // 1
        }

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


class ChildComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
             page:1
        };
    }

    someMethod = (page) => {
        this.setState({ page: page });
        this.props.onChange(page)
    }

    render() {
        return (
       <Button
            onClick={() => this.someMethod()} 
       > Click
        </Button>
      )
   }
}

Ở đây onChange là một thuộc tính với phương thức "handler" bị ràng buộc với thể hiện của nó. chúng tôi đã chuyển trình xử lý phương thức cho thành phần lớp Con, để nhận thông qua thuộc tính onChange trong đối số đạo cụ của nó.

Thuộc tính onChange sẽ được đặt trong một đối tượng đạo cụ như thế này:

props ={
onChange : this.handler
}

và được chuyển cho thành phần con

Vì vậy, thành phần Con có thể truy cập giá trị của tên trong đối tượng đạo cụ như props.onChange này

Nó được thực hiện thông qua việc sử dụng đạo cụ render.

Bây giờ, thành phần Con có một nút Bấm Click vào với một sự kiện onclick được đặt để gọi phương thức xử lý được truyền cho nó thông qua onChnge trong đối tượng đối số đạo cụ của nó. Vì vậy, bây giờ this.props.onChange in Child giữ phương thức đầu ra trong lớp Parent và các khoản tín dụng: Bits and Pieces


Xin lỗi .. vì sự chậm trễ, Ở đây onChange là một thuộc tính với phương thức "handler" bị ràng buộc với thể hiện của nó. chúng tôi đã chuyển trình xử lý phương thức cho thành phần lớp Con, để nhận thông qua thuộc tính onChange trong đối số đạo cụ của nó. Thuộc tính onChange sẽ được đặt trong một đối tượng đạo cụ như thế này: props = {onChange: this.handler} và được chuyển đến thành phần con Vì vậy, thành phần Con có thể truy cập giá trị của tên trong đối tượng đạo cụ như props.onChange này được thực hiện thông qua việc sử dụng đạo cụ render. Tham khảo và tín dụng: [ blog.bitsrc.io/ Kẻ
Preetham NT

0

Đây là cách tôi làm.

type ParentProps = {}
type ParentState = { someValue: number }
class Parent extends React.Component<ParentProps, ParentState> {
    constructor(props: ParentProps) {
        super(props)
        this.state = { someValue: 0 }

        this.handleChange = this.handleChange.bind(this)
    }

    handleChange(value: number) {
        this.setState({...this.state, someValue: value})
    }

    render() {
        return <div>
            <Child changeFunction={this.handleChange} defaultValue={this.state.someValue} />
            <p>Value: {this.state.someValue}</p>
        </div>
    }
}

type ChildProps = { defaultValue: number, changeFunction: (value: number) => void}
type ChildState = { anotherValue: number }
class Child extends React.Component<ChildProps, ChildState> {
    constructor(props: ChildProps) {
        super(props)
        this.state = { anotherValue: this.props.defaultValue }

        this.handleChange = this.handleChange.bind(this)
    }

    handleChange(value: number) {
        this.setState({...this.state, anotherValue: value})
        this.props.changeFunction(value)
    }

    render() {
        return <div>
            <input onChange={event => this.handleChange(Number(event.target.value))} type='number' value={this.state.anotherValue}/>
        </div>
    }
}

0

Hầu hết các câu trả lời được đưa ra ở trên là dành cho các thiết kế dựa trên React.Component. Nếu bạn đang sử dụng useStatetrong các bản nâng cấp gần đây của thư viện React, thì hãy làm theo câu trả lời này


-3
<Footer 
  action={()=>this.setState({showChart: true})}
/>

<footer className="row">
    <button type="button" onClick={this.props.action}>Edit</button>
  {console.log(this.props)}
</footer>

Try this example to write inline setState, it avoids creating another function.

Nó gây ra vấn đề về hiệu năng: stackoverflow.com/q/36677733/3328979
Arashsoft
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.