ReactJS: setState alt bileşen içindeki ebeveyn üzerinde


91

Bir alt bileşenden bir üst öğe üzerinde setState yapmak için önerilen model nedir.

var Todos = React.createClass({
  getInitialState: function() {
    return {
      todos: [
        "I am done",
        "I am not done"
      ]
    }
  },

  render: function() {
    var todos = this.state.todos.map(function(todo) {
      return <div>{todo}</div>;
    });

    return <div>
      <h3>Todo(s)</h3>
      {todos}
      <TodoForm />
    </div>;
  }
});

var TodoForm = React.createClass({
  getInitialState: function() {
    return {
      todoInput: ""
    }
  },

  handleOnChange: function(e) {
    e.preventDefault();
    this.setState({todoInput: e.target.value});
  },

  handleClick: function(e) {
    e.preventDefault();
    //add the new todo item
  },

  render: function() {
    return <div>
      <br />
      <input type="text" value={this.state.todoInput} onChange={this.handleOnChange} />
      <button onClick={this.handleClick}>Add Todo</button>
    </div>;
  }
});

React.render(<Todos />, document.body)

Ebeveynin durumunda tutulan bir dizi yapılacaklar listesi var. Ebeveynin durumuna erişmek ve TodoForm' handleClickbileşeninden yeni bir yapılacaklar listesi eklemek istiyorum . Benim fikrim, üst öğe üzerinde yeni eklenen yapılacaklar öğesini işleyecek bir setState yapmak.



Sadece buraya spam göndereceğim
jujiyangasli

Hata alıyorumsetState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the MyModal component.
Matt

Bağlantısız bir bileşendeState'i ayarlayamadığımla aynı hatayı alıyorum. Bunun için bir çözüm var mıydı?
Kevin Burton

Yanıtlar:


82

Üst addTodoItemöğenizde, gerekli setState'i yapacak gibi bir işlev oluşturabilir ve ardından bu işlevi alt bileşene props olarak aktarabilirsiniz.

var Todos = React.createClass({

  ...

  addTodoItem: function(todoItem) {
    this.setState(({ todos }) => ({ todos: { ...todos, todoItem } }));
  },

  render: function() {

    ...

    return <div>
      <h3>Todo(s)</h3>
      {todos}
      <TodoForm addTodoItem={this.addTodoItem} />
    </div>
  }
});

var TodoForm = React.createClass({
  handleClick: function(e) {
    e.preventDefault();
    this.props.addTodoItem(this.state.todoInput);
    this.setState({todoInput: ""});
  },

  ...

});

addTodoItemTodoForm'un handleClick'inde çağırabilirsin . Bu, üst öğe üzerinde yeni eklenen yapılacak iş öğesini oluşturacak bir setState oluşturacaktır. Umarım fikri anlarsın.

Burada keman çalın.


6
Nedir <<operatörü this.state.todos << todoItem;burada?
Gabriel Garrett

@zavtra Little Ruby kafa karışıklığı devam ediyor sanırım
azium

7
this.stateDoğrudan mutasyona uğramak kötü bir uygulamadır . Fonksiyonel setState kullanmak daha iyidir. reactjs.org/docs/react-component.html#setstate
Rohmer

2
keman kırıldı
Hunter Nelson

1
React kancaları kullanılarak bu (güncellenmiş) çözüm nasıl uygulanacak?
ecoe

11

Bunların hepsi esasen doğru, sadece temelde şunları öneren yeni (ish) resmi tepki belgelerine işaret edeceğimi düşündüm: -

Bir React uygulamasında değişen herhangi bir veri için tek bir "doğruluk kaynağı" olmalıdır. Genellikle durum, oluşturma için ona ihtiyaç duyan bileşene eklenir. Ardından, diğer bileşenlerin de buna ihtiyacı varsa, onu en yakın ortak atalarına kaldırabilirsiniz. Durumu farklı bileşenler arasında senkronize etmeye çalışmak yerine, yukarıdan aşağıya veri akışına güvenmelisiniz.

Bkz. Https://reactjs.org/docs/lifting-state-up.html . Sayfa ayrıca bir örnek üzerinden de çalışır.


8

Ana bileşende bir addTodo işlevi oluşturabilir, onu bu bağlama bağlayabilir, alt bileşene iletebilir ve oradan çağırabilirsiniz.

// in Todos
addTodo: function(newTodo) {
    // add todo
}

Sonra Todos.render'da yapardın

<TodoForm addToDo={this.addTodo.bind(this)} />

Bunu TodoForm'da şununla çağır:

this.props.addToDo(newTodo);

Bu çok faydalı oldu. bind(this)Fonksiyonu geçme anında yapmadan , böyle bir fonksiyon yok hata atıyordu this.setState is not a function.
pratpor

7

React Hook ile durumu sürdürenler için useState, yukarıdaki önerileri aşağıda bir demo kaydırıcı Uygulaması yapmak için uyarladım. Demo uygulamasında, alt kaydırıcı bileşeni ebeveynin durumunu korur.

Demo ayrıca useEffectkanca kullanıyor . (ve daha az önemlisi, useRefkanca)

import React, { useState, useEffect, useCallback, useRef } from "react";

//the parent react component
function Parent() {

  // the parentState will be set by its child slider component
  const [parentState, setParentState] = useState(0);

  // make wrapper function to give child
  const wrapperSetParentState = useCallback(val => {
    setParentState(val);
  }, [setParentState]);

  return (
    <div style={{ margin: 30 }}>
      <Child
        parentState={parentState}
        parentStateSetter={wrapperSetParentState}
      />
      <div>Parent State: {parentState}</div>
    </div>
  );
};

//the child react component
function Child({parentStateSetter}) {
  const childRef = useRef();
  const [childState, setChildState] = useState(0);

  useEffect(() => {
    parentStateSetter(childState);
  }, [parentStateSetter, childState]);

  const onSliderChangeHandler = e => {
  //pass slider's event value to child's state
    setChildState(e.target.value);
  };

  return (
    <div>
      <input
        type="range"
        min="1"
        max="255"
        value={childState}
        ref={childRef}
        onChange={onSliderChangeHandler}
      ></input>
    </div>
  );
};

export default Parent;

Bu uygulamayı create-react-app ile kullanabilir ve App.js'deki tüm kodu yukarıdaki kodla değiştirebilirsiniz.
NicoWheat

Merhaba, tepki vermek için yeniyim ve merak ediyordum: Kullanmak gerekli useEffectmi? Verileri neden hem ebeveyn hem de çocuk durumunda saklamamız gerekiyor?
grobouDu06

1
Örnekler, verileri neden hem ebeveynde hem de çocukta depolamamız gerektiğini göstermeyi amaçlamaz - çoğu zaman buna ihtiyacınız yoktur. Ancak, kendinizi çocuğun ebeveyn durumunu ayarlaması gereken bir durumda bulduysanız, bunu nasıl yapabilirsiniz. Üst durumu childState değişiminin BİR ETKİSİ OLARAK ayarlamak istiyorsanız useEffect gereklidir.
NicoWheat

3
parentSetState={(obj) => { this.setState(obj) }}

4
Bu kod soruyu yanıtlayabilirken, sorunun nasıl ve / veya neden çözüldüğüne ilişkin ek bağlam sağlamak, yanıtlayanın uzun vadeli değerini artıracaktır.
Nic3500

2

Argümanları bir alt bileşenden ana bileşene geçirmek için aşağıdaki çalışan ve basit çözümü buldum:

//ChildExt component
class ChildExt extends React.Component {
    render() {
        var handleForUpdate =   this.props.handleForUpdate;
        return (<div><button onClick={() => handleForUpdate('someNewVar')}>Push me</button></div>
        )
    }
}

//Parent component
class ParentExt extends React.Component {   
    constructor(props) {
        super(props);
        var handleForUpdate = this.handleForUpdate.bind(this);
    }
    handleForUpdate(someArg){
            alert('We pass argument from Child to Parent: \n' + someArg);   
    }

    render() {
        var handleForUpdate =   this.handleForUpdate;    
        return (<div>
                    <ChildExt handleForUpdate = {handleForUpdate.bind(this)} /></div>)
    }
}

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

JSFIDDLE'ye bakın


0

Üst öğe olarak bir sınıf bileşeniyle çalışıyorsanız, setState'i bir alt öğeye geçirmenin çok basit bir yolu onu bir ok işlevinin içinden geçirmektir. Bu, etrafından geçilebilecek yükseltilmiş bir ortam oluşturduğu için çalışır:

class ClassComponent ... {

    modifyState = () =>{
        this.setState({...})   
    }
    render(){
          return <><ChildComponent parentStateModifier={modifyState} /></>
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.