getDerivedStateFromProps,在更改道具影响下的状态更改

时间:2019-07-12 10:24:22

标签: javascript reactjs

我点击项目->我从网址:https: // app / api / v1 / asset / $ {id}获取数据。数据保存在loadItemId中。我正在将loadItemId从组件Items移至组件Details,然后移至组件AnotherItem。 每次单击Item时,道具loadItemId都会在getDerivedStateFromProps方法中发生变化。问题:我将单击Element D->在console.log'true'中看到,然后单击Element E->它显示在console.log true和{{ 1}},并且仅显示false

尝试创建三元运算符false。如果{this.state.itemX ['completed'] ? this.start () : ''}调用函数{this.state.itemX ['completed']

此处的代码:stackblitz

图片:https://imgur.com/a/OBxMKCd

项目

this.start ()

项目

class Items extends Component {
  constructor (props) {
    super(props);
    this.state = {   
      itemId: null,  
      loadItemId: ''
    }
  }

  selectItem = (id) => {
    this.setState({
      itemId: id
    })

        this.load(id);
  }

    load = (id) => {

        axios.get
            axios({
                    url: `https://app/api/v1/asset/${id}`,
                    method: "GET",
                    headers: {
                        'Authorization': `Bearer ${token}`           
                    }
            })
            .then(response => {
                    this.setState({
                            loadItemId: response.data
                    });
            })
            .catch(error => {
                    console.log(error);
            })
}

  render () {
    return (
            <div > 
                <Item
                    key={item.id}
                    item={item}
                    selectItem={this.selectItem}
                >
                <Details
                    loadItemId={this.state.loadTime}  
                /> 
            </div>
    )
  }

详细信息

class Item extends Component {
  render () {

    return (
      <div  onClick={() => this.props.selectItem(item.id}>

      </div>
    )
  } 
}

另一个项目

class Details extends Component {
    constructor() {
        super();
    }

  render () {
    return (
            <div> 
                <AnotherItem 
                    loadItemId = {this.props.loadItemId}        
                />       
            </div>
    )
  } 
}

3 个答案:

答案 0 :(得分:1)

此处:

int32_t buttons = rawController.ButtonCount();
int32_t switches = rawController.SwitchCount();
int32_t axis = rawController.AxisCount();

std::unique_ptr<bool[]> buttonsArray = std::make_unique<bool[]>(buttons);
std::vector<GameControllerSwitchPosition> switchesArray(switches);
std::vector<double> axisArray(axis);

uint64_t timestamp = rawController.GetCurrentReading(winrt::array_view<bool>(buttonsArray.get(), buttonsArray.get() + buttons), switchesArray, axisArray);

您调用setState(),然后'Item'和'Details'和'AnotherItem'调用其渲染方法。因此您会看到以前的“ loadItemId”的日志。

“加载”方法完成时。在这里:

selectItem = (id) => {
    this.setState({
        itemId: id
    })

    this.load(id);
}
再次 setState(),然后'Item'和'Details'和'AnotherItem'再次调用其render方法。此时,您会看到新的“ loadItemId”的日志。


解决方案

setState都将状态置于一个位置。加载方法完成后,而不是:

 this.setState({
     loadItemId: response.data
 });

写:

 this.setState({
     loadItemId: response.data
 });

并删除:

 this.setState({
     itemId: id,
     loadItemId: response.data
 });

来自“ selectItem”方法。

答案 1 :(得分:0)

需要澄清,但我认为我仍然可以从高层次解决这个问题。正如上面的注释中所建议的,在呈现信息的情况下,似乎您的组件AnotherItem实际上并不需要维护状态来确定调用start()方法的正确时间(尽管由于其他原因,它可能需要处于有状态状态,因为如下所述)。

您似乎要尝试实现的功能(在特定时间调用启动方法)可以仅通过componentDidUpdate生命周期方法对旧/新道具进行比较来完成。正如React文档所提供的,getDerivedStateFromProps实际上是为少数“罕见”情况保留的,我相信这里都没有。相反,似乎是要在接收到新道具并满足一定条件(例如,不等于旧道具)时调用某种方法,或者执行一些计算。可以通过挂接到componentDidUpdate来实现。

class AnotherItem extends Component {
   constructor(props) {
      super(props);
      this.state  = {}
   }

   start = () => { do something, perform a calculation }

  // Invoked when new props are passed
  componentDidUpdate(prevProps) {

     // Test condition to determine whether to call start() method based on new props,
     // (can add other conditionals limit number of calls to start, e.g., 
     // compare other properties of loadItemId from prevProps and this.props) .
     if (this.props.loadItemId && this.props.loadItemId.completed === true) {

         //Possibly store result from start() in state if needed
         const result = this.start();
      } 

    }
  }

    render () {
       // Render UI, maybe based on updated state/result of start method if 
       // needed
    );
  }
}

答案 2 :(得分:0)

您遇到此问题的原因是,您每次点击都会更改state组件中的Items

this.setState({
    itemId: id
})

在更改其state时,Items组件会重新呈现,从而导致AnotherItem的前一个state带有completed来重新呈现(因为这是子组件) true(因为您之前单击了元素D)。然后异步请求完成,并用

引起另一个重发
this.setState({
    loadItemId: response.data
});

启动另一个AnotherItem的{​​{1}}重新渲染和预期结果。

尝试删除false中的state更改,您将获得理想的结果。

我建议您阅读this article,并尝试以不同的方式构造代码。

编辑 您可以通过将加载程序添加到组件中来轻松解决此问题:

selectItem

这样,仅当获取了数据并且不会发生不必要的重新渲染时,才重新渲染selectItem = (id) => { this.setState({ itemId: id, loading: true }) this.load(id); } load = (id) => { axios.get axios({ url: `https://jsonplaceholder.typicode.com/todos/${id}`, method: "GET" }) .then(response => { this.setState({ loading: false, loadItemId: response.data }); }) .catch(error => { console.log(error); }) } render() { return ( <div > <ul> {this.state.items.map((item, index) => <Item key={item.id} item={item} selectItem={this.selectItem} /> ) } </ul> {this.state.loading ? <span>Loading...</span> : <Details itemId={this.state.itemId} loadItemId={this.state.loadItemId} />} </div> ) } 组件。