简体   繁体   English

Reactjs 组件生命周期 - 从用户输入中获取数据

[英]Reactjs Component lifecycle - Fetching data from user input

I'm new to Reactjs.我是 Reactjs 的新手。 I try to build a filters sytem from a search result.我尝试根据搜索结果构建过滤器系统。 When user select a filter I would like get new data using an AJAX call according the chosen filter.当用户选择过滤器时,我想根据所选过滤器使用 AJAX 调用获取新数据。

I set an eventHandler function on filter's checkbox wich set state of my component.我在过滤器的复选框上设置了一个 eventHandler 函数,以设置组件的状态。 This make React re-render the component.这使得 React 重新渲染组件。 But at this point, there is no newest data.但此时,还没有最新的数据。

componentWillUpdate() seems perfect for this purpose but it will be deprecated on next release (17) unless using UNSAFE_componentWillUpdate(). componentWillUpdate() 似乎非常适合此目的,但除非使用 UNSAFE_componentWillUpdate(),否则它将在下一个版本 (17) 中弃用。

How fetch newest data before re-rendering the component?在重新渲染组件之前如何获取最新数据? Which lifecycle method is better choice?哪种生命周期方法是更好的选择?

First set of data is set on my index.html file第一组数据设置在我的index.html文件中

<!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Reactjs - Filtres component</title>
</head>
<body>
<div id="filtres" class="filtres" style="width:250px;"></div>
<script type="text/javascript">
    var filtres = [{
        "date": {
            "libelle": "Year",
            "buckets": {
                "date2018": {
                "name": "date[0]",
                "value": "2018",
                "title": 2018,
                "libelle": 2018,
                "nb": 1
             },
             "date2016": {
                "name": "date[1]",
                "value": "2016",
                "title": 2016,
                "libelle": 2016,
                "nb": 54
              },
              "date2015": {
                "name": "date[2]",
                "value": "2015",
                "title": 2015,
                "libelle": 2015,
                "nb": 70
              }
            }
          }
        },
        {
         // some filters
        }
      }];
    </script>
    <script src="dist/filtresComponent.js"></script>
</body>
</html>

FiltresComponent.js过滤器组件.js

import React from 'react'
import ReactDOM from 'react-dom'
import Filtres from './Filtres'

ReactDOM.render(<Filtres filtres={filtres} />, document.getElementById('filtres'));

Filtres.js过滤器.js

import React, { Component } from 'react'
import './Filtres.css'

import Crit from './Crit'

class Filtres extends Component {
  constructor(props) {
    super(props);
    this.state = {
      filtres : this.props.filtres,
      appliedFiltres : {}
    }
  }

  addFiltre = (filtreName, filtreValue) => {
    console.log('addFiltre from FiltresComponent !!');
    console.log('We add : ');
    console.log('filtreName :' + filtreName + ' ,value : ' + filtreValue);

    this.setState((state) => {
      return {appliedFiltres: Object.assign(state.appliedFiltres, {[filtreName]: filtreValue})}
    });

    console.log(this.state);

  }

  // before re-rendering sounds good but will be deprecated on reactjs 17
  componentWillUpdate = () => {
    console.log('componentWillUpdate');
    // Fetching before rendering ?
  }


  render() {
    console.log('render Filtres.js');
    return ([
      this.state.filtres.map((crit, index) =>  {
        let libelle = crit[Object.keys(crit)].libelle;
        let critValues = Object.values(crit[Object.keys(crit)].buckets);
        return <Crit key={index} libelle={libelle} critValues={critValues} addFiltre={this.addFiltre}/>
      })
    ])
  }
}

export default Filtres

Crit.js Crit.js

import React, { Component } from 'react'


class Crit extends Component {

  static defaultProps = {
    open : true
  }

  constructor(props) {
    super(props);
    this.state = {
      open : this.props.open
    }
  }

  showHideCrit = (e) => {
    if(this.state.open){
      e.target.nextElementSibling.style.display = 'none';
      this.setState({ open: false });
    }else{
      e.target.nextElementSibling.style.display = 'flex';
      this.setState({ open: true });
    }
  }

  addFiltre = (e) => {
    console.log('addFiltre from CritComponent !!');
    // User chosen filter
    let filtreName = e.target.name;
    let filtreValue = e.target.value;
    // LiftUp
    this.props.addFiltre(filtreName, filtreValue);
  }

  render() {
    console.log('render Crit.js');
    return ([
      <div className="crit">
        <a className={"js-head_crit " + (this.state.open ? 'open' : '' )+""} onClick={this.showHideCrit}>{this.props.libelle}</a>
        <div className="crit-values">
          {
            this.props.critValues.map((critValue,index) => {
              return (                
                  <div key={index} className="crit-value" data-count={critValue.nb}>
                      <input type="checkbox" name={critValue.name} value={critValue.value} onChange={this.addFiltre}/>
                      <label className="crit-libelle">
                          {critValue.libelle}
                          <span className="crit-nb">{critValue.nb}</span>
                      </label>
                  </div>                
              )
            })
          }
          </div>
      </div>
    ])
  }
}

export default Crit

Output输出

控制台输出

Here user want to filter by year : 2015这里用户要按年份过滤:2015
I add date[2] : 2015 to appliedFiltres using this.setState我使用this.setState将 date[2] : 2015 添加到this.setState
Then I want to fetch data using the filter year = 2015然后我想使用过滤器 year = 2015 获取数据
Finally re-render component with new values最后用新值重新渲染组件

A possible solution would be to trigger the fetch operation immediately after setting the new filters in the state, you can do this via a call back to this.setState method call:一个可能的解决方案是在状态中设置新过滤器后立即触发 fetch 操作,您可以通过回调this.setState方法调用来执行此操作:

  addFiltre = (filtreName, filtreValue) => {
    // ...other codes
    this.setState(
      {},  // state update value 
      this.fetchData() // The function to trigger the AJAX call
    );
  }

Note : The re-render should happen regardless of the triggered AJAX call, you shouldn't wait for the AJAX call to complete before doing the render because this would make you app un-usable(and miserable) for a while(until the AJAX call completes).注意:无论触发的 AJAX 调用如何,重新渲染都应该发生,您不应该在渲染之前等待 AJAX 调用完成,因为这会使您的应用程序在一段时间内无法使用(并且很痛苦)(直到 AJAX呼叫完成)。

To ensure a good user experience and also avoid showing stale data, you can display maybe a loading component(which can be as simple as a text: loading... ) while the fetch from sever is happening, for that you would need a state value to track the AJAX process.为了确保良好的用户体验并避免显示过时的数据,您可以在从服务器获取时显示一个加载组件(可以像文本一样简单: loading... ),为此您需要一个状态值来跟踪 AJAX 进程。 In the code block below, I added a fetchingData boolean to the state, you can use that to track when the data fetching is happening and when it is done.在下面的代码块中,我向状态添加了一个fetchingData布尔值,您可以使用它来跟踪数据获取何时发生以及何时完成。

class Filtres extends Component {

  constructor(props) {
    // ...other codes
    this.state = {
      // ...other states
      fetchingData: false,
    };
  }

  // ...other methods

  fetchData() {
    // set fetchingData to true as soon as the AJAX process starts
    this.setState({ fetchingData: true });
    // make ajax call with set filters
    // when ajax call is done, set it back to false 
    this.setState({ fetchingData: false });
  }

  render() {
    if(this.state.fetchingData) {
      // display loading component
    } else {
      // display the current data
    }

  // ...other methods
};

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

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