Can't update state in React Context using function - javascript

I currently have two contexts - GalleryContext & AdminContext
In AdminContext, whenever I fire off handleSendImage(), I am trying to call a function (getGallery()) from GalleryContext which updates the gallery state in GalleryContext.
However, I am receiving this error:
Unhandled Rejection (TypeError): this.setState is not a function
Could anyone please advise me on how to fix this? Much appreciated in advance!
Here's my code:
GalleryContext:
class GalleryProvider extends Component {
state = {
gallery: []
};
getGallery() {
let database = firebase
.database()
.ref("/gallery/")
.once("value")
.then(images => {
console.log(images.val());
this.setState(
{
gallery: images.val()
},
() => {}
);
});
}
componentDidMount() {
this.getGallery();
}
render() {
return (
<GalleryContext.Provider
value={{
...this.state,
getGallery: this.getGallery
}}>
{this.props.children}
</GalleryContext.Provider>
);
}
}
AdminContext.js
class AdminProvider extends Component {
static contextType = GalleryContext;
state = {
upload_image: null,
gallery_title: null,
gallery_description: null,
gallery_filename: null,
progress: null
};
handleSendImage = (event, gallery, user) => {
event.preventDefault();
const { upload_image } = this.state;
firebase
.storage()
.ref(`images/${upload_image.name}`)
.put(upload_image)
.on(
"state_changed",
snapshot => {
// progrss function ....
const progress = Math.round(
(snapshot.bytesTransferred / snapshot.totalBytes) * 100
);
this.setState({
progress
});
},
error => {
// error function ....
console.log(error);
},
() => {
// complete function ....
firebase
.storage()
.ref("images")
.child(upload_image.name)
.getDownloadURL()
.then(url => {
const newImage = {
description: this.state.gallery_description,
download_url: url,
file_name: this.state.gallery_filename,
id: gallery.length,
uploader_uid: user.uid
};
firebase
.database()
.ref("/gallery/")
.child(gallery.length)
.set(newImage)
.then(() => {
this.context.getGallery();
})
.catch(error => {
console.log(error);
});
});
}
);
};
render() {
return (
<AdminContext.Provider
value={{
...this.state
handleSendImage: this.handleSendImage
}}>
{this.props.children}
</AdminContext.Provider>
);
}
}

Since you're using states, you would need a constructor for your class component and need to bind your methods within the constructor. I suggest you to try the following code:
class AdminProvider extends Component {
static contextType = GalleryContext;
constructor(props) {
super(props)
this.state = {
upload_image: null,
gallery_title: null,
gallery_description: null,
gallery_filename: null,
progress: null
};
this.handleSendImage = this.handleSendImage.bind(this);
}
handleSendImage = (event, gallery, user) => {
event.preventDefault();
const { upload_image } = this.state;
firebase
.storage()
.ref(`images/${upload_image.name}`)
.put(upload_image)
.on(
"state_changed",
snapshot => {
// progrss function ....
const progress = Math.round(
(snapshot.bytesTransferred / snapshot.totalBytes) * 100
);
this.setState({
progress
});
},
error => {
// error function ....
console.log(error);
},
() => {
// complete function ....
firebase
.storage()
.ref("images")
.child(upload_image.name)
.getDownloadURL()
.then(url => {
const newImage = {
description: this.state.gallery_description,
download_url: url,
file_name: this.state.gallery_filename,
id: gallery.length,
uploader_uid: user.uid
};
firebase
.database()
.ref("/gallery/")
.child(gallery.length)
.set(newImage)
.then(() => {
this.context.getGallery();
})
.catch(error => {
console.log(error);
});
});
}
);
};
render() {
return (
<AdminContext.Provider
value={{
...this.state
handleSendImage: this.handleSendImage
}}>
{this.props.children}
</AdminContext.Provider>
);
}
}
Similarly:
class GalleryProvider extends Component {
constructor(props) {
super(props);
this.state = {
gallery: []
};
this.getGallery = this.getGallery.bind(this);
}
getGallery() {
let database = firebase
.database()
.ref("/gallery/")
.once("value")
.then(images => {
console.log(images.val());
this.setState(
{
gallery: images.val()
},
() => {}
);
});
}
componentDidMount() {
this.getGallery();
}
render() {
return (
<GalleryContext.Provider
value={{
...this.state,
getGallery: this.getGallery
}}>
{this.props.children}
</GalleryContext.Provider>
);
}
}
Alternatively, you could use React Hooks inside functional components. Hope this solves your problem.
I would suggest you to read React's documentation on Constructor for more information.

Related

Reac js TypeError: this.state.data.map is not a function

class CardList extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
};
}
componentDidMount() {
firestore
.collection('users')
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
this.setState({ data: doc.data() });
});
});
}
render() {
return (
<div className="cardlist">
{this.state.data.email
? this.state.data.map((data) => {
return <div>{this.state.data.email}</div>;
})
: console.log('error')}
</div>
);
}
}
TypeError: this.state.data.map is not a function
I want to take out the emails in the Firestore and print them out, but I can't print them because of typeerror. Why is there an error?
console.log(this.state.data) result is
{ createdAt: t, name: 'good', email: 'good#gmail.com', isTutor: 'off' };
{ name: 'joe', isTutor: 'on', email: 'joe#gmail.com', createdAt: t };
You are not assigning value to your array properly, rather you should do like the code below. I've also refactored the code in render function.
class CardList extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
};
}
componentDidMount() {
firestore
.collection('users')
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
this.setState({ data: [...this.state.data, doc.data()] });
});
});
}
render() {
return (
<div className="cardlist">
{this.state.data &&
this.state.data.map((item) => {
return <div>{item.email}</div>;
})}
</div>
);
}
}
this.setState({ data: doc.data() }); - you are not adding to the state, but replacing it with an object for each doc. And you cannot .map an object, thus the error.

In React app , I delete my component data on firebase but it does'n delete from my dom even when I change the state

I have get my data from firebase , loop through them and display them to dom.
then I added a delete button and send a delete request using axios and it's delete from firebase but the dom doesn't rerender. I set a deleting state to change it in 'then' block but even when I change the state it dosn't rerender!
what can I do?
class Orders extends Component {
state = {
orders: [],
loading: true,
deleting: false,
};
componentDidMount() {
axios
.get('/order.json')
.then((res) => {
// console.log(res.data);
const fetchedOrders = [];
for (let key in res.data) {
fetchedOrders.push({ ...res.data[key], id: key });
}
this.setState({ loading: false, orders: fetchedOrders });
})
.catch((err) => {
this.setState({ loading: false });
});
}
deleteHandler = (id) => {
axios.delete(`/order/${id}.json`).then((res) => {
this.setState({ deleting: true });
console.log(res, this.state.deleting);
});
};
render() {
return (
<div>
{this.state.orders.map((order) => (
<Order
key={order.id}
ingredients={order.ingredient}
price={order.price}
id={order.id}
delete={() => this.deleteHandler(order.id)}
/>
))}
</div>
);
}
}
You have to update the orders state while calling deleteHandler! Try this code!
import React from 'react';
import axios from 'axios';
// YOUR OTHER IMPORT GOES HERE
class Orders extends Component {
constructor(props) {
this.state = {
orders: [],
loading: true,
deleting: false,
}
}
componentDidMount() {
axios
.get('/order.json')
.then((res) => {
// console.log(res.data);
const fetchedOrders = [];
for (let key in res.data) {
fetchedOrders.push({ ...res.data[key], id: key });
}
this.setState({ loading: false, orders: fetchedOrders });
})
.catch((err) => {
this.setState({ loading: false });
});
}
deleteHandler = (id) => {
this.setState({
orders: this.state.orders.filter(orderValue => orderValue.id !== id)
})
axios.delete(`/order/${id}.json`).then((res) => {
this.setState({ deleting: true });
console.log(res, this.state.deleting);
});
};
render() {
return (
<div>
{this.state.orders.map((order) => (
<Order
key={order.id}
ingredients={order.ingredient}
price={order.price}
id={order.id}
delete={() => this.deleteHandler(order.id)}
/>
))}
</div>
);
}
}

I got navigation as undefined in react navigation 5?

I have a reusable component for Sign in with Apple Button
After user success, i navigate hem to Home screen
But i notes when i log navigation it's log undefined,
and when i log this.props i just got the two actions i made in redux!
So how can i access to navigation in this component and why it's not accessed by default!
Log
props => {"isLogin": [Function isLogin], "storeToken": [Function storeToken]}
navigation => undefined
Code
import appleAuth, {
AppleAuthCredentialState,
AppleAuthError,
AppleAuthRealUserStatus,
AppleAuthRequestOperation,
AppleAuthRequestScope,
AppleButton,
} from '#invertase/react-native-apple-authentication';
import React from 'react';
import {ActivityIndicator, StyleSheet, View} from 'react-native';
import {connect} from 'react-redux';
import API from '../../api/API';
import {isLoginFunc} from '../../redux/actions/isLoginAction';
import {saveToken} from '../../redux/actions/saveTokenAction';
class AppleAuth extends React.Component {
constructor(props) {
super(props);
this.authCredentialListener = null;
this.user = null;
this.state = {
credentialStateForUser: -1,
loading: false,
};
}
componentDidMount() {
const {navigation} = this.props;
console.log('did-navigation', navigation);
console.log('did- this.props', this.props);
/**
* subscribe to credential updates.This returns a function which can be used to remove the event listener
* when the component unmounts.
*/
this.authCredentialListener = appleAuth.onCredentialRevoked(async () => {
// console.warn('Credential Revoked');
this.fetchAndUpdateCredentialState().catch(error =>
this.setState({credentialStateForUser: `Error: ${error.code}`}),
);
});
this.fetchAndUpdateCredentialState()
.then(res => this.setState({credentialStateForUser: res}))
.catch(error =>
this.setState({credentialStateForUser: `Error: ${error.code}`}),
);
}
componentWillUnmount() {
/**
* cleans up event listener
*/
this.authCredentialListener();
}
signIn = async () => {
// start a login request
try {
const appleAuthRequestResponse = await appleAuth.performRequest({
requestedOperation: AppleAuthRequestOperation.LOGIN,
requestedScopes: [
AppleAuthRequestScope.EMAIL,
AppleAuthRequestScope.FULL_NAME,
],
});
this.setState({loading: true});
const {
user: newUser,
email,
nonce,
fullName: {familyName, givenName},
identityToken,
realUserStatus /* etc */,
} = appleAuthRequestResponse;
let username = `${givenName} ${familyName}`;
this.user = newUser;
this.fetchAndUpdateCredentialState()
.then(res => {
this.setState({credentialStateForUser: res});
console.log('res:::', res);
})
.catch(error => {
console.log(`Error: ${error.code}`);
this.setState({credentialStateForUser: `Error: ${error.code}`});
});
if (identityToken) {
console.log('email', email);
console.log('username', username);
console.log('nonce', nonce);
this.sendData(email, username, nonce);
// e.g. sign in with Firebase Auth using `nonce` & `identityToken`
} else {
// no token - failed sign-in?
}
if (realUserStatus === AppleAuthRealUserStatus.LIKELY_REAL) {
console.log("I'm a real person!");
}
// console.warn(`Apple Authentication Completed, ${this.user}, ${email}`);
} catch (error) {
if (error.code === AppleAuthError.CANCELED) {
alert('User canceled Apple Sign in');
// console.warn('User canceled Apple Sign in.');
} else {
console.error(error);
}
}
};
fetchAndUpdateCredentialState = async () => {
if (this.user === null) {
this.setState({credentialStateForUser: 'N/A'});
} else {
const credentialState = await appleAuth.getCredentialStateForUser(
this.user,
);
if (credentialState === AppleAuthCredentialState.AUTHORIZED) {
this.setState({credentialStateForUser: 'AUTHORIZED'});
} else {
this.setState({credentialStateForUser: credentialState});
}
}
};
// Send data "name,image,email" to API
sendData = async (Email, Name, Id) => {
try {
let response = await API.post('/apple', {
email: Email,
name: Name,
id: Id,
});
let {
data: {
data: {
response: {token},
},
},
} = response;
console.log('token:?>:', token);
console.log('props', this.props);
console.log('navigation', this.props.navigation);
this.setState({loading: false});
this.props.storeToken(token);
this.props.isLogin(true);
// this.props.navigation.push('BottomTabNavigator');
} catch (err) {
console.log(err);
alert('Unexpected Error, try again later.');
this.setState({loading: false});
}
};
render() {
return (
<View style={styles.container}>
{this.state.loading ? (
<ActivityIndicator />
) : (
<AppleButton
style={styles.appleButton}
cornerRadius={5}
buttonStyle={AppleButton.Style.WHITE}
buttonType={AppleButton.Type.SIGN_IN}
onPress={() => this.signIn()}
/>
)}
</View>
);
}
}
const styles = StyleSheet.create({
appleButton: {
width: 200,
height: 50,
// margin: 10,
},
container: {
flex: 1,
justifyContent: 'center',
},
});
const mapDispatchToProps = dispatch => {
// to excute the actions we want to invok
return {
isLogin: isLogin => {
dispatch(isLoginFunc(isLogin));
},
storeToken: token => {
dispatch(saveToken(token));
},
};
};
export default connect(
null,
mapDispatchToProps,
)(AppleAuth);
-
singin.js
<AppleAuth /> in the render method
if you render your component as component, not as a navigation screen, it will not receive navigation prop. It was like this in all versions of react-navigation
Access the navigation prop from any component

React Expected an assignment or function call and instead saw an expression

I'm trying to render the data from my database get this instead Failed to compile.
./src/components/list-pets.component.js
Line 38:5: Expected an assignment or function call and instead saw an expression no-unused-expressions
Search for the keywords to learn more about each error.enter code here
Here is my code from the trouble component
import React, { Component } from 'react';
import axios from 'axios';
export default class ListPets extends Component {
constructor(props) {
super(props);
this.state = {
pets: []
};
}
componentDidMount = () => {
this.getPets();
};
getPets = () => {
axios.get('http://localhost:5000/pets')
.then((response) => {
const data = response.data;
this.setState({ pets: data });
console.log('Data has been received!');
})
.catch((err) => {
console.log(err);
});
}
displayPet = (pets) => {
if (!pets.length) return null;
return pets.map((pet, index) => {
<div key={index}>
<h3>{pet.name}</h3>
<p>{pet.species}</p>
</div>
});
};
render() {
console.log('State: ', this.state);
return (
<div className='adopt'>
{this.displayPet(this.state.pets)}
</div>
)
}
}
You need to return a value at each pets.map iteration, currently you’re returning undefined.
return pets.map((pet, index) => {
return (
<div key={index}>
<h3>{pet.name}</h3>
<p>{pet.species}</p>
</div>
)
});
You have to wait until fetching data is completed.
You should have to define the loading bar while fetching.
class App extends Component {
constructor() {
super();
this.state = {
pageData: {},
loading: true
}
this.getData();
}
async getData(){
const res = await fetch('/pageData.json');
const data = await res.json();
return this.setState({
pageData: data,
loading: false
});
}
componentDidMount() {
this.getData();
}
render() {
const { loading, pageData } = this.state;
if (loading){
return <LoadingBar />
}
return (
<div className="App">
<Navbar />
</div>
);
}
}

Firebase Query in React - I Can Log to Console, But Cannot Use in Component

I'd like to see in this component users with their photos.
I can see id's of each user, so database.getUserList function works! But there is something wrong with getAdditionalUserInfoById function.
import { database } from "../firebase";
class UsersInfo extends Component {
render() {
let users = database.getUserList(this.props.id);
return (
<div>
<List>
{Object.keys(users).map(key => (
<ListItem>
<Avatar>
<ImageAvatars
photoUrl={database.getAdditionalUserInfoById(
users[key]["userId"],
"photoUrl"
)}
/>
</Avatar>
<ListItemText
primary={users[key]["userId"]}
secondary="Jan 9, 2014"
/>
</ListItem>
))}
</List>
</div>
);
}
}
I have getAdditionalUserInfoById in another file that queries firebase. With this console.log command, I can see the correct photoUrl value of each user in console, but in the component above it is undefined. How can I get it to UsersInfo component, what should be fixed here?
export const getAdditionalUserInfoById = (userId, query) => {
var userDbRef = database.ref("users/" + userId);
userDbRef
.child("additionalInfo")
.child(query)
.on("value", function(snapshot) {
console.log("userInfoById: ", snapshot.val());
return snapshot.val();
});
};
Firebase DB Structure:
"users" : {
"h06c4wAxn0eeN3yQ4Qw9DfEVww03" : {
"additionalInfo" : {
"photoUrl" : "https://thumb.ibb.co/iVW1y9/Screen_Shot_2018_05_10_at_12_23_59_PM.jpg"
}
}
}
***** UPDATE *****
class InfoWindowContent extends Component {
constructor(props) {
super(props);
this.state = {
users: [],
userPhotos: []
};
}
componentDidMount() {
this.setState({ users: database.showUserListbyGeoId(this.props.id) });
var userPhotos = [];
Object.keys(this.state.users).map(function(key) {
userPhotos[
this.state.users[key]["userId"]
] = database.getAdditionalUserInfoById(
this.state.users[key]["userId"],
"photoUrl"
);
});
this.setState({ userPhotos });
}
render() {
const { users, userPhotos } = this.state;
return (
<div>
<List>
{Object.keys(users).map(key => (
<ListItem>
<Avatar>
<ImageAvatars
photoUrl={this.state.userPhotos[users[key]["userId"]]}
/>
</Avatar>
<ListItemText
primary={users[key]["userId"]}
secondary={"31 Dec, 2035"}
/>
</ListItem>
))}
</List>
</div>
);
}
}
My Firebase Database Functions
// ../firebase.js
export const showUserListbyGeoId = cityId => {
var userList = [];
var cityUserListRef = database.ref("cities/" + cityId + "/users");
cityUserListRef.orderByChild("userId").on("value", function(snapshot) {
if (snapshot.exists()) {
snapshot.forEach(function(childSnapshot) {
var key = childSnapshot.key;
userList[key] = childSnapshot.val();
});
}
});
return userList;
};
export const getAdditionalUserInfoById = (userId, query, callback) => {
var userDbRef = database.ref("users/" + userId);
userDbRef
.child("additionalInfo")
.child(query)
.on("value", snapshot => {
return callback(snapshot.val());
});
};
database.getUserList is async function. You must wait for data returning.
The solution is, define a state in component. After the data returned, you would setState, and the view would be updated.
I don't know about your database.getUserList use callback or Promise. If it return the Promise, the example like this:
class UsersInfo extends Component {
constructor(props) {
super(props);
this.state = { users: [] };
}
componentDidMount() {
database.getUserList(this.props.id)
.then((results) => {
this.setState({ users: results });
});
}
public render() {
const users = this.state.users;
...
}
}
For getAdditionalUserInfoById, because it is event listener, you can use callback
export const getAdditionalUserInfoById = (userId, query, callback) => {
var userDbRef = database.ref('users/' + userId);
userDbRef.child("additionalInfo").child(query).on("value", (snapshot) => {
callback(snapshot.val());
});
})
}
componentDidMount() {
const self = this;
getAdditionalUserInfoById(userId, query, (value) => {
// set state or do something
self.setState({ user: value })
})
}
UPDATE:
// ../firebase.js
export const showUserListbyGeoId = (cityId, callback) => {
const userList = [];
const cityUserListRef = database.ref("cities/" + cityId + "/users");
cityUserListRef.orderByChild("userId").on("value", (snapshot) => {
if (snapshot.exists()) {
snapshot.forEach((childSnapshot) => {
userList.push(childSnapshot.val());
});
}
return callback(userList);
});
};
export const getAdditionalUserInfoById = (userId, query) => {
var userDbRef = database.ref("users/" + userId);
return userDbRef
.child("additionalInfo")
.child(query)
.once("value")
.then((snapshot) => ({
[query]: snapshot.val(),
userId,
}));
};
class InfoWindowContent extends Component {
constructor(props) {
super(props);
this.state = {
users: [],
userPhotos: []
};
}
componentDidMount() {
const self = this;
database.showUserListbyGeoId(this.props.id, (userList) => {
self.setState(userList);
return Promise.all(
userList.map(({ userId }) => database.getAdditionalUserInfoById(
userId,
"photoUrl"
))
).then((userPhotos) => {
self.setState({ userPhotos })
});
});
}
render() {
const { users, userPhotos } = this.state;
return (
<div>
<List>
{userPhotos.map((photo) => (
<ListItem>
<Avatar>
<ImageAvatars
photoUrl={photo.photoUrl}
/>
</Avatar>
<ListItemText
primary={photo.userId}
secondary={"31 Dec, 2035"}
/>
</ListItem>
))}
</List>
</div>
);
}
}

Categories

Resources