Estoy tratando de analizar los datos recibidos de firebase y pasar estos nuevos datos a un componente. Los datos se obtienen de forma asíncrona con promesas. El problema que encuentro es que no se muestra la primera vez, aunque configuré componentDidMount y llamé a la función para analizar los datos allí.

Los datos que recibo de firebase son los siguientes:

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

Como puede ver, necesito analizar los datos para reemplazar el type de fallas con el nombre correcto de types.

Para hacerlo, tengo una función que analiza los datos y establece un estado. Este es el código para eso:

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});
  }

Esta función se llama en componentDidMount y en componentWillReceiveProps, pero cuando el componente se monta, no se muestran datos. Sin embargo, cuando el componente recibe nuevos accesorios, se muestra correctamente.

¿Estoy haciendo algo mal? También intenté llamar a esta función en componentWillMount pero no funciona para el primer render.

Actualizar

Según lo solicitado, este es el código del componente:

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;

Este es el código responsable de manejar la recuperación de datos que se maneja en el componente primario:

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});
    });
  }

El código para la función get es:

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;
  });
}

Actualización 2:

He notado algo muy muy extraño. Si vuelvo a ordenar un poco de código en el 'componenteDidMount' del componente principal y primero recupero la lista de tipos y luego obtengo la lista de fallas, la primera representación se realiza correctamente pero los tipos no se analizan, así que ' Me quedan celdas vacías en mi mesa.

0
César Alberca 16 feb. 2017 a las 13:28

3 respuestas

La mejor respuesta

Si entiendo su componente correctamente, FaultList no hace nada de forma asincrónica y simplemente recibe accesorios del padre. Dado eso, puede simplificar las cosas haciendo FaultList sin estado y simplemente analizando los datos en render directamente. El componente se volverá a procesar (es decir: se llamará a render) cada vez que el componente reciba nuevos accesorios y los datos se volverán a analizar.

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;
0
ericgio 16 feb. 2017 a las 11:08

Encontré cuál era el problema. Como Bartek mencionó que el componente Tabla debería tener una referencia al estado, por lo que todo lo que tengo que hacer es mover parseFields a el componente padre y lo llama cada vez que firebase recupera nuevos datos. Este es el código para eso:

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;
0
Community 23 may. 2017 a las 12:25

Creo que debería usar this.state.data en su componente Table en lugar de this.state.faults:

  <Table
    data={this.state.data}
    columns={this.columns}
    handleClick={this.props.handleClick}
    />
0
Bartek Fryzowicz 16 feb. 2017 a las 10:51