Сделать React Redux Firebase асинхронным - PullRequest
0 голосов
/ 01 мая 2018

Привет всем!

У меня проблемы с redux и firebase. Обязательно ли ставить роутеры на корневые компоненты проектов?

Некоторые из моих приложений очень хорошо работают с этой системой, но не с функциями asynchronous, в основном с функциями firebase.

.

Этот пост в основном о том, как и где разместить firebase.[objects], чтобы они всегда читались правильно. Официальная документация не очень четкая и понятная.

Напоминаю Я новичок в пожарной базе .

Я пытался поместить их в функции цикла реакции (например, componentDidMount(), ...), так как какой-то триггер после рендера, но ничего не сделал.

По сути, это то, что я сделал:

Service.jsx

function getAll() {
    // I had saved some values in the localStorage
    const uid = JSON.parse(localStorage.getItem('user')).uid;

    // Here the reference to appropriate child in the database
    const dbRef = firebase.database().ref().child('events');

    // Choosing the user with appropriate's is
    const userRef = dbRef.child(uid);

    // An array to save fetched values
    let answer = [];

    // Created a listener
    userRef.on('value', snap => {
        let rx = snap.val();
        for (let item in snap.val()) {
            answer.push(rx[ item ]);
        }

        // Return a promise for the actions
        return Promise.resolve({
            events: answer
        });
    });

    return Promise.resolve({
        events: answer
    });

}

Reducer.jsx

export function events(state = { events: [] }, action) {
    //    --------> Get all
    case eventConstants.GET_ALL_REQUEST:
        return {
            ...state,
            managingEvent: true,
        };
    case eventConstants.GET_ALL_SUCCESS:
        return {
            ...state,
            events: action.events
        };
    case eventConstants.GET_ALL_FAILURE:
        return state;
    default:
        return state
    }
}

Action.jsx

function getAll() {
    return dispatch => {
        dispatch(request());

        eventService.getAll()
            .then(
                ans => {
                    dispatch(success(ans.events));
                },
                error => {
                    dispatch(failure(error));
                    dispatch(alertActions.error(error));
                }
            );
    };

    function request() {
        return { type: eventConstants.GET_ALL_REQUEST }
    }

    function success(events) {
        return { type: eventConstants.GET_ALL_SUCCESS, events }
    }

    function failure(error) {
        return { type: eventConstants.GET_ALL_FAILURE, error }
    }
}

Grob:

Это то, что я сделал. Теперь я пытаюсь сделать следующее: получить данные, используя store.dispatch(Action.getAll()), потому что я использую эти данные в компоненте поиска. Я использую их таким образом:

Поиск

// usual imports here, connect inculed
import React, { Component } from 'react';
import { connect } from 'react-redux';

class SearchPage extends Component {

    // So I first used the constructor
    constructor(props){
        this.props.dispatch(Action.getAll());
    }

    componentDidMount() {
        // Then here... an same for all others methods of react lifecycle
        this.props.dispatch(Action.getAll());
    }

    render() {

    // TODO Don't forget the translation
    let { translate, events } = this.props;

    ....

    const table = (
        <Table hover>
            {tableHeader}
            <tbody>
            {events.map(item =>
                <tr key={item.key}>
                    <th scope="row">{item.key}</th>
                    <td>{item.name}</td>
                    <td>{item.startDate}</td>
                    <td>{item.endDate}</td>
                </tr>
            )}
            </tbody>
        </Table>
    );
...
}


const mapStateToProps = (state) => {
    const { events } = state.events;
    const translate = getTranslate(state.locale);
    return {
        events,
        translate,
    };
}

const connectedPage = connect(mapStateToProps)(SearchPage);
export { connectedPage as SearchPage };

Marks:

Иногда это работало оштрафовано, затем, когда я обновил базу данных онлайн, а затем снова сделал рендеринг, он сказал, что event был нулевым или неопределенным, что я мог понять из-за перехода в Reducer.js.

But what should I do now ? теперь мой вопрос.

Спасибо за чтение этого текста и спасибо за вашу помощь.

:)

Ответы [ 2 ]

0 голосов
/ 02 мая 2018

Проблема в том, что функция getAll внутри службы не будет ждать завершения слушателя. Из-за этого переменная ответа всегда будет иметь [].

Решение состоит в том, чтобы вернуть new promise instance, которое разрешается только после получения данных от firebase.

Обновлен Service.jsx

function getAll () {
  // I had saved some values in the localStorage
  const uid = JSON.parse(localStorage.getItem('user')).uid;

  // Here the reference to appropriate child in the database
  const dbRef = firebase.database().ref().child('events');

  // Choosing the user with appropriate's is
  const userRef = dbRef.child(uid);

  // An array to save fetched values
  const answer = [];
  return new Promise((resolve) => {
    // Created a listener
    userRef.on('value', (snap) => {
      const rx = snap.val();
      for (const item in snap.val()) {
        answer.push(rx[item]);
      }

      // Return a promise for the actions
      resolve({
        events: answer
      });
    });
  });
}
0 голосов
/ 02 мая 2018

Я полагаю, что return Promise... в конце getAll () (за пределами userRef.on('value'...) в Services.jsx заставляет функцию getAll возвращать неопределенное значение, если выборка событий еще не завершена. Я бы реорганизовал Service.jsx следующим образом (методы Firebase уже возвращают обещания, так что вы можете вернуть их напрямую):

function getAll() {
    const uid = JSON.parse(localStorage.getItem('user')).uid;
    const dbRef = firebase.database().ref().child('events');
    const userRef = dbRef.child(uid);

    let answer = [];
    return userRef.on('value', snap => {
      snapshot.forEach((snap) => answer.push(snap.val())); 
      return {
        events: answer
      };
    });
}

Другой подход, который вы можете попробовать, - это создать специальный компонент, который ничего не рендерит, отвечающий только за присоединение слушателя firebase к componentDidMount и отсоединение его от componentWillUnmount (что-то вроде кода ниже), и они импортируют этот специальный компонент внутрь. ваш SearchPage.jsx.

class FirebaseListenToEvents extends Component {
  componentDidMount() {
    const uid = JSON.parse(localStorage.getItem('user')).uid;
    const dbRef = firebase.database().ref().child('events');    
    this.userRef = dbRef.child(uid);

    this.userRef.on('value', (snap) => {
      this.props.onUpdateEvent(snap.key, snap.val());
    });
  }

  componentWillUnmount() {
    this.userRef.off('value');
  }

  render() {
    return null;
  }
}

Я не уверен, какой подход подойдет вам лучше, но я надеюсь, что это даст вам некоторое представление о том, как работать с Firebase & React. Эта статья также, кажется, объясняет это лучше меня. Если у вас есть проблемы, просто спросите меня в комментариях. Удачи!

...