I'm facing some issues with redux
and firebase
. Is it mandatory to put the routers to the root components of the projects?
Some of mine work very well with this system, but not the asynchronous
functions, mainly those of firebase
.
This post is basically about how and where to place firebase.[objects]
so that they are always read correctly. Official documentation is not very explicit and clear.
I remind you I'm new to firebase .
I tried to place them in the react cycle functions (such as componentDidMount()
,...) since some trigger after render, but nothing did.
Basically, this is what I did:
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 }
}
}
This is what I've done. Now there's what I'm trying to do: retrieve datas using store.dispatch(Action.getAll())
because I use those data in the search Component. I use them in this way:
Search
// 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 };
Sometimes it worked fined, then when I updated the database online and then it re-rendered, it said that event
was null or undefined, which I could understand, because of the transition in Reducer.js
.
But what should I do now ?
is now my question.
Thank you for reading this text and thank you for your help.
:)
The problem here is that the getAll
function inside the service will not wait for listener to complete. Because of this, the answer variable will always have []
.
Solution is to return a new promise instance
which gets resolved only after receiving the data from firebase.
Updated 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
});
});
});
}
I believe the return Promise...
at the end of getAll() (outside the userRef.on('value'...
) on Services.jsx causes the function getAll to return undefined if the fetch of events is not completed yet. I would refactor Service.jsx like this (Firebase methods return promises already, so you can return them directly):
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
};
});
}
Another approach you may try is to create a special component that does not render anything, being only responsible for attaching a firebase listener on componentDidMount and detaching it on componentWillUnmount (something like the code below), and them import this special component inside your 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;
}
}
I'm not sure which approach will work best for you but I hope this gives you some insights on how to deal with Firebase & React. This article also seems to explain it better than me. If you have problems just ask me on the comments. Good luck!
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.