He estado atrapado en este problema durante horas. Quiero implementar la arquitectura Flux. Estoy tratando de crear una lista de tareas pendientes. Sin embargo, quiero cargar algunos datos iniciales de antemano. Por ejemplo en mi todoStore.js:

import { EventEmitter } from "events";

class ToDoStore extends EventEmitter {
    constructor(){
        super();
        this.bucket_list = [{
            id: 123,
            name: "Hi",
            isCompleted: false
        }]

    }

    getAll(){
        return this.bucket_list;
    }
}

Tengo algunos datos iniciales aquí que son utilizados por mi todo.js:

import toDoStore from './stores/todoStore'

class BucketApp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            bucket_list: toDoStore.getAll()
        };
    }

Y esto funciona bien y elegante. Tengo una tienda que es básicamente un collection del que mi component recibe datos. Sin embargo, ahora quiero inicializar los datos de una base de datos. Así que actualicé mi todoStore.js:

class BucketlistStore extends EventEmitter {
    constructor(){
        super();
        fetch(url)
            .then(d => d.json())
            .then(d => {
                this.bucket_list = d;
            });
    }

    getAll(){
        return this.bucket_list;
    }
}

Sin embargo, getAll () devuelve undefined. ¿Por qué es este el caso? ¿Qué estoy haciendo mal?

1
anderish 30 oct. 2017 a las 23:15

4 respuestas

La mejor respuesta

Devuelve undefined porque la obtención de datos es asíncrona y durante la inicialización this.bucket_list no está definida. Prueba esto:

class BucketlistStore extends EventEmitter {
    constructor(){
        super();
        this.bucket_list_promise = fetch(url)
            .then(d => d.json());
    }

    getAll(){
        return this.bucket_list_promise;
    }
}

Entonces

import toDoStore from './stores/todoStore'

class BucketApp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            loadingData: true,
            bucket_list: null
        };
    }

    componentWillMount(){
       toDoStore.getAll().then(result => {
          this.setState({ bucket_list: result, loadingData: false }) 
       })
    }
1
Alexander Vitanov 30 oct. 2017 a las 20:33

Dado que fetch es una operación asincrónica, ¿quizás llame a getAll() demasiado pronto? No es una solución, pero puede verificar la suposición:

class BucketlistStore extends EventEmitter {
    constructor(){
        super();
        this.loading = true;
        fetch(url)
            .then(d => d.json())
            .then(d => {
                this.loading = false;
                this.bucket_list = d;
            });
    }

    getAll(){
        console.log(this.loading);
        return this.bucket_list;
    }
}

Si es cierto, sugeriría simplemente no representar BucketApp mientras loading es true. Ponga la bandera para almacenar y úsela en BucketApp para evitar el renderizado (muestre el cargador en su lugar).

1
dhilt 30 oct. 2017 a las 20:24

Para resolver este problema, cargo los datos iniciales en el estado de mi componente y escucho emitir en componentDidMount para actualizar el estado cuando se resuelve la promesa. Debajo de un ejemplo con su código.

//Store

import EventEmitter from "events";
let initState = {
  id: 123,
  name: "Hi",
  isCompleted: false,
  loadingData: true
};

class BucketlistStore extends EventEmitter {
  constructor() {
    super();
    fetch(url)
      .then(d => d.json())
      .then(d => {
        const { id, name, isCompleted } = d;
        initState = { id, name, isCompleted, loadingData: false };
        this.emit("updated");
      });
  }

  getAll() {
    return initState;
  }
}

export default new BucketlistStore();

//Component

import React, { Component } from "react";
import toDoStore from "./stores/todoStore";

class BucketApp extends Component {
  constructor(props) {
    super(props);
    this.state = toDoStore.getAll();
  }

  updateState = () => {
    const { id, name, isCompleted, loadingData } = toDoStore.getAll();
    this.setState({ id, name, isCompleted, loadingData });
  };

  componentWillMount() {
    toDoStore.on("updated", this.updateState);
  }

  componentWillUnmount(){
    toDoStore.off("updated", this.updateState);
  }

  render() {
    const { id, name, isCompleted, loadingData } = this.state;

    if (loadingData) return <p>Loading...</p>;

    return null; //your code
  }
}
0
Carla França 13 ago. 2019 a las 22:39

Supongo que sucede porque su componente se renderiza antes de finalizar la recuperación asíncrona. En otras palabras, obtiene indefinición, y cuando obtiene el resultado después de terminar fetch (cuando se actualiza su tienda) su componente no se actualiza. Puede aplicar las técnicas descritas anteriormente, O puede usar Redux. En Redux, si la tienda se actualiza, causa actualizaciones de todos los componentes que de alguna manera están conectados con la tienda Redux.

0
user8685433 30 oct. 2017 a las 20:38