I'm learning react and I'm having difficulty putting data from Firebase into the application state. I use Rebase but I am open to any solution!
still have an error similar to this one :
Thank you for your help !
Here is the code :
class App extends Component {
state = {
user:'',
vampires: {}
}
componentDidMount() {
if(this.state.user === ''){
firebase.auth().onAuthStateChanged(user => {
if(user){
this.handleUserLogin({ user })
this.setVampires({ user })
} else {
console.log('error')
}
})
}
}
setVampires = async (authData) => {
console.log(this.state.user)
await base.fetch(`/${authData.user.uid}/vampires`, { context: this })
.then(data => {
console.log(data)
let vampires = this.state.vampires;
vampires = {..._.cloneDeep(data)};
this.setState({vampires: {vampires}})
})
}
handleUserLogin = async authData => {
this.setState({user: authData.user.uid})
}
Your Firebase data is returned as an Object, with properties VampM5-..... React expects that you pass any repeated data as an array, not as an object.
So you'll need to convert the data from Firebase to an array, for example with:
await base.fetch(`/${authData.user.uid}/vampires`, { context: this })
.then(data => {
vampires = [];
data.forEach((child) => {
vampires.push({ ...child.val(), ".key": child.key });
})
this.setState({ vampires: vampires })
})
Related
I need help retrieving firebase data in React Native, using expo.
This code works fine when I refresh the app, but when it first starts, it throws an error:
Error: Error: Client is offline.
Maybe I need to do it async and await, I have tried some ways, but no success.
componentDidMount = async () => {
var radioFireData = null;
const { names } = this.props;
const dbRef = ref(db, "records/");
get(child(dbRef, "flap/"))
.then((snapshot) => {
if (snapshot.exists()) {
radioFireData = snapshot.val();
this.setState({ checked: radioFireData[names] });
} else {
console.log("No data available");
}
})
.catch((error) => {
console.log(error);
});
};
Here it is... Maybe I can help someone.
componentDidMount(){
const { names } = this.props;
const reference = ref(db, "records/" + "/flap/");
onValue(
reference,
(snapshot) => {
const data = snapshot.val();
this.setState({ checked: data[names] });
},
{
onlyOnce: true,
}
);
};
I want to pass the return value to my component. I wrote this service using firebase. When calling the geLoggedInUser method in my component it returns undefined. Ideally, I want to pass the doc.id to my component from my service please help;
SERVICE
docId: any;
getLoggedInUser() {
this.fireAuth.onAuthStateChanged((user) => {
if (user) {
console.log(user.email, 'user is valid');
const db = collection(this.firestore, 'users');
getDocs(db).then((resp) => {
this.data = [
...resp.docs.map((item) => {
return { ...item.data(), id: item.id };
}),
];
//filter users array to get current logged in users data
this.authUser = this.data.filter((item: any) => {
this.docId = user.uid;
return item.email === user.email;
});
});
} else {
this.router.navigate(['/login']);
return;
}
});
return this.docId;
}
COMPONENT
ngOnInit(): void {
console.log(this.auth.getLoggedInUser());
}
I recommend get involved in the Promise and Observable concepts.
https://firebase.google.com/docs/reference/js/v8/firebase.auth.Auth
Your problem is when you call getLoggedInUser() is doings this:
return this.docId;
Calls this.fireAuth.onAuthStateChanged which returns a Promise, wait and then execute the rest of the response.
What you can do is use an observable variable in the SERVICE and connect to it from the component.
Please read the following Firebase: Should Promise or Observable be used in an Angular project?
I hope this helps!
In angular, when you use services it’s not common to use variable to pass data to components. To that you should use a observables or promises/await/async.
In this case, I would recommend you use a observable. Here you have an example how you can implement it:
docIdSubject = new BehaviorSubject<any>(null);
Public get docId(){
return docIdSubject.asObservable();
}
getLoggedInUser() {
this.fireAuth.onAuthStateChanged((user) => {
if (user) {
console.log(user.email, 'user is valid');
const db = collection(this.firestore, 'users');
getDocs(db).then((resp) => {
this.data = [
...resp.docs.map((item) => {
return { ...item.data(), id: item.id };
}),
];
//filter users array to get current logged in users data
this.authUser = this.data.filter((item: any) => {
//this.docId = user.uid
return item.email === user.email;
};
this.docIdSubject.next(user.uid);
});
} else {
this.router.navigate(['/login']);
return;
}
})
}
ngOnInit(): void {
this.auth.docId.subscribe(res =>
console.log(res);
);
this.auth.getLoggedInUser();
}
I'm trying to keep session stayed logged in after refreshing the browser. The user data that is being fetched is not rendering after being fetched. The console is saying "Cannot read properties of undefined (reading 'user'). This is my code for the login/sign up page.
The data I'm trying to access is in the picture below:
(Auth.js)
const Auth = () => {
const navigate = useNavigate();
const dispatch = useDispatch();
const [isSignup, setIsSignup] = useState(false);
const [inputs, setInputs] = useState({
name: "",
username: "",
email: "",
password: ""
})
const handleChange = (e) => {
setInputs(prevState => {
return {
...prevState,
[e.target.name]: e.target.value
}
})
}
const sendRequest = async (type = '') => {
const res = await axios.post(`/user/${type}`, {
name: inputs.name,
email: inputs.email,
username: inputs.username,
password: inputs.password,
}).catch(error => console.log(error))
const data = await res.data;
console.log(data)
return data;
}
const handleSubmit = (e) => {
e.preventDefault()
console.log(inputs)
if (isSignup) {
sendRequest("signup")
.then((data) => {
dispatch(authActions.login());
localStorage.setItem('userId', data.user._id);
navigate("/posts");
});
} else {
sendRequest("login")
.then((data) => {
dispatch(authActions.login());
localStorage.setItem('userId', data.user._id);
navigate("/posts");
});
}
}
Redux store file
const authSlice = createSlice({
name: "auth",
initialState: { isLoggedIn: false },
reducers: {
login(state) {
state.isLoggedIn = true
},
logout(state) {
state.isLoggedIn = false
}
}
})
export const authActions = authSlice.actions
export const store = configureStore({
reducer: authSlice.reducer
})
Chaining promises using .then() passes the resolved value from one to the next. With this code...
sendRequest("...")
.then(() => dispatch(authActions.login()))
.then(() => navigate("/posts"))
.then(data => localStorage.setItem('token', data.user))
You're passing the returned / resolved value from navigate("/posts") to the next .then() callback. The navigate() function returns void therefore data will be undefined.
Also, your redux action doesn't return the user so you can't chain from that either.
To access the user data, you need to return it from sendRequest()...
const sendRequest = async (type = "") => {
try {
const { data } = await axios.post(`/user/${type}`, { ...inputs });
console.log("sendRequest", type, data);
return data;
} catch (err) {
console.error("sendRequest", type, err.toJSON());
throw new Error(`sendRequest(${type}) failed`);
}
};
After that, all you really need is this...
sendRequest("...")
.then((data) => {
dispatch(authActions.login());
localStorage.setItem('userId', data.user._id);
navigate("/posts");
});
Since you're using redux, I would highly recommend moving the localStorage part out of your component and into your store as a side-effect.
I have a function "sendMessage" in React class:
class MessageForm extends React.Component {
...
sendMessage = async () => {
const { message } = this.state;
if (message) {
this.setState({ loading: true });
if (this.props.isPrivateChannel === false) {
socket.emit("createMessage", this.createMessage(), (response) => {
this.setState({ loading: false, message: "", errors: [] });
});
} else {
if (this.state.channel && this.state.channel._id === undefined) {
socket.emit("createChannelPM", this.state.channel, async (response) => {
const chInfo = { ...response, name: this.props.currentChannel.name };
console.log("chInfo : ", chInfo);
await this.props.setCurrentChannel(chInfo).then((data) => {
if (data) {
console.log("data : ", data);
console.log("this.props.currentChannel : ", this.props.currentChannel);
}
});
});
}
...
function mapStateToProps(state) {
return {
isPrivateChannel: state.channel.isPrivateChannel,
currentChannel: state.channel.currentChannel,
};
}
const mapDispatchToProps = (dispatch) => {
return {
setCurrentChannel: async (channel) => await dispatch(setCurrentChannel(channel)),
}
};
Here, in sendMessage function, I retrieve "response" from socket.io, then put this data into variable "chInfo" and assign this to Redux state, then print it right after assinging it.
And Redux Action function, "setCurrentChannel" looks like:
export const setCurrentChannel = channel => {
return {
type: SET_CURRENT_CHANNEL,
payload: {
currentChannel: channel
}
};
};
Reducer "SET_CURRENT_CHANNEL" looks like:
export default function (state = initialState, action) {
switch (action.type) {
case SET_CURRENT_CHANNEL:
return {
...state,
currentChannel: action.payload.currentChannel
};
...
The backend Socket.io part look like (I use MongoDB):
socket.on('createChannelPM', async (data, callback) => {
const channel = await PrivateChannel.create({
...data
});
callback(channel)
});
The console.log says:
Problem : The last output, "this.props.currentChannel" should be same as the first output "chInfo", but it is different and only print out previous value.
However, in Redux chrome extension, "this.props.currentChannel" is exactly same as "chInfo":
How can I get and use newly changed Redux states immediately after assinging it to Redux State?
You won't get the updated values immediately in this.props.currentChannel. After the redux store is updated mapStateToProps of MessageForm component is called again. Here the state state.channel.currentChannel will be mapped to currentChannel. In this component you get the updated props which will be accessed as this.props.currentChannel.
I believe you want to render UI with the latest data which you which you can do.
This is my database structure below tutorCopy is the currentId of the user on basis of which I have to retrieve the user email but the problem is I can't get it, I have tried two methods but both are not working:
1st method with promise
componentWillMount(){
let user = firebase.auth().currentUser.uid;
const emailFetch = ["useremail"]
const emailpromise = emailFetch.map(id => {
return firebase.database().ref("tutorCopy/").child(user).child(id).on('value', s => s)
})
Promise.all(emailpromise)
.then(user => {
this.setState({ markers: s.values(s.val()) })
})
.catch(err => {
console.log(err)
})
}
Other one with snapshot:
componentWillMount(){
var user = firebase.auth().currentUser.uid;
var currId = JSON.stringify(user);
firebase.database().ref("tutorCopy/").child('user').once("value", snapshot => {
this.setState({ markers: Object.values(snapshot.val()) })
})
}