Bagaimana cara memperbarui status induk di Bereaksi?

350

Struktur saya terlihat sebagai berikut:

Component 1  

 - |- Component 2


 - - |- Component 4


 - - -  |- Component 5  

Component 3

Komponen 3 harus menampilkan beberapa data tergantung pada keadaan Komponen 5. Karena alat peraga tidak dapat diubah, saya tidak bisa hanya menyimpannya dalam Komponen 1 dan meneruskannya, bukan? Dan ya, saya sudah membaca tentang redux, tetapi tidak ingin menggunakannya. Saya berharap bahwa mungkin untuk menyelesaikannya hanya dengan bereaksi. Apakah aku salah?

wklm
sumber
20
super-mudah: meneruskan parent-setState-Function via properti ke komponen-anak: <MyChildComponent setState = {p => {this.setState (p)}} /> Dalam komponen-anak, panggil melalui this.props. setState ({myObj, ...});
Marcel Ennix
@MarcelEnnix, Komentar Anda menghemat waktu saya. Terima kasih.
Dinith Minura
<MyChildComponent setState={(s,c)=>{this.setState(s, c)}} />jika Anda akan menggunakan retasan ini, pastikan Anda mendukung panggilan balik.
Barkermn01
4
Melewati panggilan balik untuk mengatur status induk adalah praktik yang sangat buruk yang dapat menyebabkan masalah pemeliharaan. Itu memecahkan enkapsulasi dan itu membuat komponen 2 4 dan 5 erat digabungkan ke 1. Jika berjalan jalan ini maka Anda tidak akan dapat menggunakan kembali komponen anak ini di tempat lain. Lebih baik Anda memiliki alat peraga khusus sehingga komponen anak dapat memicu peristiwa setiap kali sesuatu terjadi, maka komponen induk akan menangani peristiwa itu dengan benar.
Pato Loco
@MarcelEnnix, mengapa kurung keriting di sekitar this.setState(p)? Saya mencoba tanpa mereka dan tampaknya berhasil (Saya sangat baru untuk Bereaksi)
Biganon

Jawaban:

678

Untuk komunikasi anak-orang tua Anda harus melewati fungsi pengaturan negara dari orang tua ke anak, seperti ini

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

Dengan cara ini anak dapat memperbarui keadaan orang tua dengan panggilan fungsi yang diteruskan dengan alat peraga.

Tetapi Anda harus memikirkan kembali struktur komponen Anda, karena seperti yang saya pahami komponen 5 dan 3 tidak terkait.

Salah satu solusi yang mungkin adalah membungkusnya dalam komponen level yang lebih tinggi yang akan berisi keadaan komponen 1 dan 3. Komponen ini akan mengatur status level yang lebih rendah melalui alat peraga.

Ivan
sumber
6
mengapa Anda memerlukan this.handler = this.handler.bind (this) dan bukan hanya fungsi handler yang menetapkan status?
chemook78
35
@ chemook78 dalam metode kelas Bereaksi ES6 tidak otomatis mengikat ke kelas. Jadi jika kita tidak menambahkan this.handler = this.handler.bind(this)konstruktor, thisdi dalam handlerfungsi akan mereferensikan penutupan fungsi, bukan kelas. Jika tidak ingin mengikat semua fungsi Anda di konstruktor, ada dua cara lagi untuk mengatasinya dengan menggunakan fungsi panah. Anda bisa menulis handler klik sebagai onClick={()=> this.setState(...)}, atau Anda dapat menggunakan inisialisasi properti bersama dengan fungsi panah seperti yang dijelaskan di sini babeljs.io/blog/2015/06/07/react-on-es6-plus di bawah "Fungsi panah"
Ivan
1
Berikut adalah contoh tindakan ini: plnkr.co/edit/tGWecotmktae8zjS5yEr?p=preview
Tamb
5
Itu semua masuk akal, mengapa e.preventDefault? dan apakah itu memerlukan jquery?
Vincent Buscarello
1
Pertanyaan singkat, apakah ini melarang penugasan negara bagian di dalam anak?
ThisGuyCantEven
50

Saya menemukan solusi kerja berikut untuk meneruskan argumen fungsi onClick dari child ke komponen induk:

Versi dengan meneruskan metode ()

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

Lihatlah JSFIDDLE

Versi dengan melewatkan fungsi Panah

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

Lihatlah JSFIDDLE

Roma
sumber
Yang ini bagus! dapatkah Anda menjelaskan bagian ini: <ChildB handleToUpdate = {handleToUpdate.bind(this)} />Mengapa harus mengikat lagi?
Dane
@Dane - Anda harus mengikat konteks ini untuk menjadi orang tua sehingga ketika thisdipanggil di dalam anak, thismerujuk pada keadaan orang tua dan bukan anak. Ini adalah penutupan yang terbaik!
Casey
@ Casey tapi bukankah kita melakukan itu di konstruktor? Dan bukankah itu cukup ??
Dane
Anda sepenuhnya benar! Saya melewatkan itu. Ya, jika Anda sudah melakukannya di konstruktor maka Anda baik untuk pergi!
Casey
Anda adalah pasangan legenda! Ini akan menjaga komponen terkandung dengan baik tanpa harus dipaksa untuk membuat komponen induk untuk menangani pertukaran negara
adamj
13

Saya ingin mengucapkan terima kasih kepada jawaban yang paling banyak dipilih untuk memberi saya ide masalah saya sendiri pada dasarnya variasi dengan fungsi panah dan melewati param dari komponen anak:

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

Semoga ini bisa membantu seseorang.

Ardhi
sumber
apa yang istimewa tentang fungsi panah melalui panggilan langsung?
Ashish Kamble
@AshishKamble thisfungsi tanda panah mengacu pada konteks induk (yaitu Parentkelas).
CPHPython
Ini adalah jawaban rangkap. Anda dapat menambahkan komentar ke jawaban yang diterima dan menyebutkan tentang fitur eksperimental ini untuk menggunakan fungsi panah di kelas.
Arashsoft
10

Saya suka jawaban mengenai fungsi lewat sekitar, itu teknik yang sangat berguna.

Di sisi lain Anda juga dapat mencapai ini menggunakan pub / sub atau menggunakan varian, operator, seperti Flux . Teorinya super sederhana, minta komponen 5 mengirim pesan yang komponen 3 dengarkan. Komponen 3 kemudian memperbarui keadaannya yang memicu render. Ini membutuhkan komponen stateful, yang, tergantung pada sudut pandang Anda, mungkin atau mungkin tidak anti-pola. Saya menentang mereka secara pribadi dan lebih suka sesuatu yang lain mendengarkan pengiriman dan mengubah status dari atas-ke-bawah (Redux melakukan ini, tetapi menambahkan terminologi tambahan).

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

Bundel pengirim dengan Flux sangat sederhana, ia hanya mendaftarkan panggilan balik dan memanggil mereka ketika ada pengiriman, melewati isi pada pengiriman (dalam contoh singkat di atas tidak ada payloaddengan pengiriman, hanya sebuah id pesan). Anda dapat mengadaptasi ini ke pub / sub tradisional (mis. Menggunakan EventEmitter dari acara, atau versi lain) dengan sangat mudah jika itu lebih masuk akal bagi Anda.

Matt Styles
sumber
Komponen Reacts saya "berjalan" di browser seperti pada tutorial resmi ( facebook.github.io/react/docs/tutorial.html ) Saya mencoba memasukkan Flux dengan browserify, tetapi browser mengatakan, Dispatcher itu tidak ditemukan :(
wklm
2
Sintaks yang saya gunakan adalah sintaks modul ES2016 yang memerlukan transpilasi (saya menggunakan Babel , tetapi ada yang lain, transformasi babelify dapat digunakan dengan browserify), ia mentransformasikannya kevar Dispatcher = require( 'flux' ).Dispatcher
Matt Styles
8

Saya menemukan solusi kerja berikut untuk meneruskan argumen fungsi onClick dari child ke komponen induk dengan param:

kelas induk:

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

kelas anak:

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>
    )
} }
H. parnian
sumber
2
Adakah yang bisa mengatakan apakah itu solusi yang dapat diterima (terutama tertarik melewati parameter seperti yang disarankan).
ilans
param1hanya ditampilkan pada konsol tidak dapat menetapkan itu selalu ditugaskantrue
Ashish Kamble
Saya tidak dapat berbicara dengan kualitas solusinya, tetapi ini berhasil melewati param untuk saya.
James
6

Ketika Anda perlu berkomunikasi antara anak ke orang tua di tingkat bawah, maka lebih baik memanfaatkan konteks . Dalam komponen induk, tentukan konteks yang dapat dipanggil oleh anak seperti

Komponen induk dalam komponen case Anda 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
}

Sekarang dalam komponen anak (komponen 5 dalam kasus Anda), katakan saja komponen ini bahwa ia ingin menggunakan konteks induknya.

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

Anda dapat menemukan proyek Demo di repo

Rajan Twanabashu
sumber
Saya tidak dapat memahami jawaban ini, dapatkah Anda menjelaskan lebih lanjut tentang hal itu
Ashish Kamble
5

Tampaknya kita hanya bisa meneruskan data dari orangtua ke anak karena reaksi mempromosikan Aliran Data Searah, tetapi untuk membuat pembaruan orangtua itu sendiri ketika sesuatu terjadi dalam "komponen anak" -nya, kita biasanya menggunakan apa yang disebut "fungsi panggilan balik".

Kami meneruskan fungsi yang ditentukan dalam induk ke anak sebagai "alat peraga" dan memanggil fungsi itu dari anak yang memicunya dalam komponen induk.


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>

Dalam contoh ini kita dapat membuat data pass dari SubChild -> Child -> Parent dengan meneruskan fungsi ke Child langsungnya.

Vishal Bisht
sumber
4

Saya telah menggunakan jawaban teratas dari halaman ini berkali-kali, tetapi ketika mempelajari Bereaksi, saya telah menemukan cara yang lebih baik untuk melakukan itu, tanpa mengikat dan tanpa fungsi inline di dalam alat peraga.

Lihat saja di sini:

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

Kuncinya ada di fungsi panah:

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

Anda dapat membaca lebih lanjut di sini . Semoga ini bermanfaat bagi seseorang =)

Александр Немков
sumber
Ini jauh lebih masuk akal bagi saya. Terima kasih!
Brett Rowberry
3

-Kita dapat membuat ParentComponent dan dengan metode handleInputChange untuk memperbarui status ParentComponent. Impor komponen ChildComponent dan kami melewati dua alat peraga dari komponen induk ke anak, ie.handleInputChange, fungsi dan jumlah.

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} />
    );
  }
}
  • Sekarang kita buat file ChildComponent dan simpan sebagai ChildComponent.jsx. Komponen ini tidak memiliki kewarganegaraan karena komponen anak tidak memiliki status. Kami menggunakan pustaka jenis prop untuk memeriksa jenis props.

    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;
Sachin Metkari
sumber
Bagaimana cara kerjanya ketika anak memiliki anak yang mempengaruhi penyangga orang tuanya?
Bobort
3

Jika skenario yang sama ini tidak menyebar di mana-mana Anda dapat menggunakan konteks Bereaksi, khususnya jika Anda tidak ingin memperkenalkan semua overhead yang diperkenalkan oleh perpustakaan manajemen negara. Plus, lebih mudah dipelajari. Tapi hati-hati, Anda bisa menggunakannya secara berlebihan dan mulai menulis kode yang buruk. Pada dasarnya Anda mendefinisikan komponen Wadah (yang akan menampung dan mempertahankan bagian keadaan itu untuk Anda) membuat semua komponen tertarik untuk menulis / membaca bagian data anak-anaknya (tidak harus anak langsung)

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

Anda juga bisa menggunakan Bereaksi polos dengan benar.

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

lulus doSomethingAbout5 hingga Komponen 1

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

Jika ini masalah umum, Anda harus mulai berpikir memindahkan seluruh keadaan aplikasi ke tempat lain. Anda memiliki beberapa opsi, yang paling umum adalah:

https://redux.js.org/

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

Pada dasarnya, alih-alih mengelola status aplikasi pada komponen Anda, Anda mengirim perintah ketika sesuatu terjadi untuk mendapatkan status diperbarui. Komponen menarik status dari wadah ini juga sehingga semua data terpusat. Ini tidak berarti tidak bisa menggunakan keadaan lokal lagi, tapi itu topik yang lebih maju.

Pato Loco
sumber
2

jadi, jika Anda ingin memperbarui komponen induk,

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

Di sini onChange adalah atribut dengan metode "handler" yang terikat pada instance itu. kami melewati penangan metode ke komponen kelas Anak, untuk menerima melalui properti onChange dalam argumen properti.

Atribut onChange akan ditetapkan dalam objek props seperti ini:

props ={
onChange : this.handler
}

dan diteruskan ke komponen anak

Jadi komponen Child dapat mengakses nilai nama dalam objek props seperti props.onChange ini

Ini dilakukan melalui penggunaan render props.

Sekarang komponen Child memiliki tombol "Klik" dengan event onclick diatur untuk memanggil metode handler yang diteruskan melalui onChnge dalam objek argumen propsnya. Jadi sekarang this.props.onChange in Child memegang metode output di kelas Induk Referensi dan kredit: Bits and Pieces

Preetham NT
sumber
Maaf .. untuk keterlambatan, Here onChange adalah atribut dengan metode "handler" yang terikat pada instance itu. kami melewati penangan metode ke komponen kelas Anak, untuk menerima melalui properti onChange dalam argumen properti. Atribut onChange akan ditetapkan dalam objek props seperti ini: props = {onChange: this.handler} dan diteruskan ke komponen child Sehingga komponen Child dapat mengakses nilai nama dalam objek props seperti props.onChange ini dilakukan melalui penggunaan render props. Referensi dan kredit: [ blog.bitsrc.io/…
Preetham NT
0

Ini caraku melakukannya.

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>
    }
}
SAYA YANG TERBAIK
sumber
0

Sebagian besar jawaban yang diberikan di atas adalah untuk desain berbasis React.Component. Jika Anda menggunakan useStatepemutakhiran terbaru dari React library, ikuti jawaban ini

leeCoder
sumber
-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.
Suresh
sumber
Ini menyebabkan masalah kinerja: stackoverflow.com/q/36677733/3328979
Arashsoft