简体   繁体   English

触发子组件的重新渲染

[英]Trigger Re-Render of Child component

I'm new to React and am running into the same problem a few times. 我是React的新手,我遇到了几次同样的问题。 In this particular situation, I'm trying to get an option in a select dropdown to update when I update a text input. 在这种特殊情况下,我正在尝试在选择下拉列表中获取一个选项,以便在更新文本输入时进行更新。

I have a parent, App, with the state attribute "directions", which is an array. 我有一个父,App,状态属性为“directions”,这是一个数组。 This gets passed as a property to a child, GridSelector, which creates the text field and dropdown. 它作为属性传递给子GridSelector,后者创建文本字段和下拉列表。 When the text field is changed, a function triggers to update the parent state. 更改文本字段时,函数会触发更新父状态。 This in turn causes the GridSelector property to update. 这反过来导致GridSelector属性更新。 However, the dropdown values, which are originally generated from that GridSelector property, do not re-render to reflect the new property value. 但是,最初从该GridSelector属性生成的下拉值不会重新呈现以反映新的属性值。

I'm trying to figure out the most React-ful way to do this and similar manuevers. 我正在试图找出最有效的方法来做这个和类似的操作。 In the past, I've set a state in the child component, but I think I've also read that is not proper. 在过去,我在子组件中设置了一个状态,但我想我也读过这个不合适的状态。

My working site is at amaxalaus.bigriverwebdesign.com 我的工作地点在amaxalaus.bigriverwebdesign.com

Here's the pertinent code from each file: 这是每个文件的相关代码:

App.js App.js

class App extends React.Component {
    constructor(props){
      super(props);
      this.state = {
       directions: [],
       dataRouteDirections: '/wp-json/wp/v2/directions',
       currentDirectionsIndex: 0
     }
     this.addImageToGrid = this.addImageToGrid.bind(this);
     this.changeTitle=this.changeTitle.bind(this);
   }


 componentDidMount(){
    fetch(this.state.dataRouteDirections)
        .then(data => data=data.json())
        .then(data => this.setState({directions:data}));

  }

  addImageToGrid(image) {
    this.refs.grid.onAddItem(image);  //passes image add trigger from parent to child
  }

  createNewDirections(){
    var directions= this.state.directions;
    var index = directions.length;
    var lastDirections = directions[directions.length-1];
    var emptyDirections= {"id":0,"acf":{}};
    emptyDirections.acf.grid="[]";
    emptyDirections.acf.layout="[]";
    emptyDirections.title={};
    emptyDirections.title.rendered="New Directions";

    if (lastDirections.id!==0 ) { ///checks if last entry is already blank
      this.setState({
        directions: directions.concat(emptyDirections),  //adds empty directions to end and updates currentdirections
        currentDirectionsIndex: index
      });
    }
  }

  changeTitle(newTitle){
    var currentDirections = this.state.directions[this.state.currentDirectionsIndex];
    currentDirections.title.rendered = newTitle;
  }

  render() {
    var has_loaded;  //has_loaded was added to prevent double rendering during loading of data from WP
    this.state.directions.length > 0 ? has_loaded = 1 : has_loaded = 0;

    if (has_loaded ) {
     /* const currentGrid = this.state.directions;*/
    return (  //dummy frame helpful for preventing redirect on form submit

        <div>

          <div className="fullWidth alignCenter container">
            <GridSelector 
              directions={this.state.directions} 
              currentDirectionsIndex={this.state.currentDirectionsIndex} 
              changeTitle={this.changeTitle}
            />
          </div>
          <Grid ref="grid" 
            currentGrid={this.state.directions[this.state.currentDirectionsIndex]}
          />
          <ImageAdd addImageToGrid={this.addImageToGrid}/>
          <div className="fullWidth alignCenter container">
            <button onClick={this.createNewDirections.bind(this)}> Create New Directions </button>
          </div>

        </div>

      )
    } else {
      return(
        <div></div>
      )
    }
  }
}

GridSelector.js GridSelector.js

class GridSelector extends React.Component {    

  constructor(props) {
    super(props);
    var currentDirections = this.props.directions[this.props.currentDirectionsIndex];
    this.state = {
        currentTitle:currentDirections.title.rendered
    }
  }
    createOption(direction) {
        if (direction.title) {
            return(
                <option key={direction.id}>{direction.title.rendered}</option>
            )
        }   else {
            return(
                <option></option>
            )
        }
    }    

    handleChangeEvent(val) {
        this.props.changeTitle(val);  //triggers parent to update state
    }    

    render() {

        return(
            <div>
                <select name='directions_select'>
                    {this.props.directions.map(direction => this.createOption(direction))}              
                </select>    

                <div className="fullWidth" >
                    <input 
                        onChange={(e)=>this.handleChangeEvent(e.target.value)}
                        placeholder={this.state.currentTitle}
                        id="directionsTitle"
                    />
                </div>
            </div>
        )
    }
}

You made a very common beginner mistake. 你犯了一个很常见的初学者错误。 In React state should be handled as an immutable object. 在React状态下应该作为不可变对象处理。 You're changing the state directly, so there's no way for React to know what has changed. 你正在直接改变状态,所以React无法知道改变了什么。 You should use this.setState . 你应该使用this.setState

Change: 更改:

  changeTitle(newTitle){
    var currentDirections = this.state.directions[this.state.currentDirectionsIndex];
    currentDirections.title.rendered = newTitle;
  }

To something like: 对于这样的事情:

  changeTitle(newTitle){
    this.setState(({directions,currentDirectionsIndex}) => ({
         directions: directions.map((direction,index)=> 
         index===currentDirectionsIndex? ({...direction,title:{rendered:newTitle}}):direction
    })

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM