React:我应该在哪个生命周期钩子上解析新收到的数据?

时间:2017-02-16 10:28:02

标签: javascript reactjs

我正在尝试解析从firebase接收的数据并将此新数据传递给组件。数据与promises异步获取。我遇到的问题是它没有第一次呈现,即使我已经设置componentDidMount并调用函数来解析那里的数据。

我从firebase收到的数据如下:

faults: [{
  name: "Foo",
  status: "Open"
  type: "type1"
},
{
  name: "Bar",
  status: "Open"
  type: "type2"
}],
types: [{
  key: "type1",
  type: "Accident"
},
{
  key: "type2",
  type: "Crash"
}]

如您所见,我需要解析数据以使用type中的正确名称替换types个错误。

为此,我有一个解析数据并设置状态的函数。这是代码:

parseFields() {
    let parsedFields = [];
    this.props.fields.forEach((field) => {
      const typeSelected= this.props.estados.find(element => element.key === field.type) || '';

      let parsedField = Object.assign({}, field);

      parsedField['type'] = typeSelected.type;

      parsedFields.push(parsedField);
    });
    this.setState({data: parsedFields});
  }

此功能在componentDidMountcomponentWillReceiveProps上调用,但是当组件安装时,不会显示任何数据。但是,当组件收到新的道具时,它会正确显示。

我做错了吗?我也尝试在componentWillMount上调用此函数,但它不适用于第一个渲染。

更新

根据要求,这是组件代码:

import React from 'react'

import Table from './Table';

class FaultList extends React.Component {
  constructor(props) {
    super(props);

    this.columns = [
      {
        name: 'Name',
        field: 'name'
      },
      {
        name: 'Type',
        field: 'type'
      },
      {
        name: 'Status',
        field: 'status'
      }
    ];

    this.state = {
      faults: []
    };

    this.parseFields = this.parseFields.bind(this);
  }

  parseFields() {
    let parsedFields = [];
    this.props.fields.forEach((field) => {
      const typeSelected= this.props.types.find(element => element.key === field.type) || '';

      let parsedField = Object.assign({}, field);

      parsedField['type'] = typeSelected.type;

      parsedFields.push(parsedField);
    });
    this.setState({data: parsedFields});
  }

  componentDidMount() {
    this.parseFields();
  }

  componentWillReceiveProps() {
    this.parseFields();
   }

  render() {
    return(
      <Table
        data={this.state.faults}
        columns={this.columns}
        handleClick={this.props.handleClick}
        />
    );
  }
}

export default FaultList;

这是负责处理在父组件中处理的数据提取的代码:

componentDidMount() {
    this.faultsRef= ref.child('faults');

    this.faultsRef.on('value', (snap) => {
      let faults= [];
      snap.forEach((child) => {
        let fault= child.val();
        let key = child.key;
        const finalFault = update(fault, {$merge: {key}});
        faults.push(finalFault);
      });
      this.setState({faults});
    });

    get('types')
    .then((types) => {
      this.setState({types});
    });
  }

get函数的代码是:

function get(node) {
  return ref.child(node).once('value')
  .then((snap) => {
    let list = [];

    snap.forEach((child) => {
      let object = child.val();
      let key = child.key;
      const finalObject = update(object , {$merge: {key}});
      list .push(finalObject);
    });

    return list;
  });
}

更新2:

我注意到一些非常奇怪的事情。如果我在父组件的'componentDidMount'中重新排序一些代码,我首先检索类型列表然后获取错误列表,第一次重新渲染是正确完成但是类型没有被解析所以我'我的桌子上留下了空单元格。

3 个答案:

答案 0 :(得分:0)

我认为您应该在this.state.data组件而不是Table上使用this.state.faults

  <Table
    data={this.state.data}
    columns={this.columns}
    handleClick={this.props.handleClick}
    />

答案 1 :(得分:0)

如果我正确理解您的组件,FaultList不会异步执行任何操作,只需从父级接收道具即可。鉴于此,您可以通过使FaultList无状态并仅直接解析render中的数据来简化操作。每当组件接收到新的道具时,组件将重新渲染(即:render将被调用),并且数据将被重新解析。

class FaultList extends React.Component {
  constructor(props) {
    super(props);

    this.columns = [
      ...
    ];

    this.parseFields = this.parseFields.bind(this);
  }

  // You no longer need `componentDidMount` and `componentWillReceiveProps`

  parseFields() {
    let parsedFields = [];
    this.props.fields.forEach((field) => {
      const typeSelected= this.props.types.find(element => element.key === field.type) || '';

      let parsedField = Object.assign({}, field);
      parsedField['type'] = typeSelected.type;
      parsedFields.push(parsedField);
    });

    // Note: we are now returning the value instead of setting `state`.
    return parsedFields;
  }

  render() {        
    return(
      <Table
        data={this.parseFields()}
        columns={this.columns}
        handleClick={this.props.handleClick}
      />
    );
  }
}

export default FaultList;

答案 2 :(得分:0)

我发现了问题所在。正如Bartek提到Table组件应该具有对状态的引用所以我所要做的就是将parseField移动到父组件并在每次firebase检索新数据时调用它。这是代码:

class FaultsView extends React.Component {
  constructor(props) {
    super(props);

    this.faultsRef = null;

    this.state = {
      faults: [],
      parsedFaults: [],
      types: []
    };

    this.parseFields = this.parseFields.bind(this);
  }

  componentDidMount() {
    get('types')
    .then((types) => {
      this.setState({types});
    });

    this.faultsRef = ref.child('faults');

    this.faultsRef.on('value', (snap) => {
      let faults= [];
      snap.forEach((child) => {
        let fault= child.val();
        let key = child.key;
        const finalFault = update(fault, {$merge: {key}});
        faults.push(finalFault);
      });
      this.setState({faults});
      // This was the fix!
      this.parseFields();
    });
  }

  componentWillUnMount() {
    this.faultsRef.off();
  }

  parseFields() {
    let parsedFields = [];
    this.props.fields.forEach((field) => {
      const typeSelected= this.props.types.find(element => element.key === field.type) || '';

      let parsedField = Object.assign({}, field);

      parsedField['type'] = typeSelected.type;

      parsedFields.push(parsedField);
    });
    this.setState({data: parsedFields});
  }

  render() {
    return (
      <div>
        <FaultList
          faults={this.state.parsedFaults}
          handleClick={this.handleClick}/>
      </div>
    );
  }
}

export default FaultsView;