Getting an Empty array from the firestore - javascript

This is my code for LeftBar Component
I want to get the data stored in the "contacts" document in the firebase but getting an empty array. Idk know why this is happening. And one thing if anyone can tell me how to do it with class-based component ( how to use that use effect thing into ComponentDidMount and ComponentDidUpdate) as I have two other class Components which are also using the same functionality Please help
import React, {useState , useEffect} from "react";
import { Avatar, IconButton } from "#material-ui/core";
import AddCircleIcon from "#material-ui/icons/AddCircle";
import MoreIcon from "#material-ui/icons/More";
import ChatBubbleOutlineIcon from "#material-ui/icons/ChatBubbleOutline";
import SearchIcon from '#material-ui/icons/Search';
import LeftChats from './LeftChats';
import "./LeftBar.css";
import db from './firebase'
function LeftBar () {
const [contacts, setContacts] = useState([])
useEffect(() => {
db.collection("contacts").onSnapshot((snapshot)=> setContacts(
snapshot.docs.map((doc)=> ({
id : doc.id,
data : doc.data(),
}))
))
},[])
console.log(contacts);
return (
<div className="leftbar">
<div className="left-header">
<Avatar />
<div className="left-right-header">
<IconButton>
<AddCircleIcon />
</IconButton>
<IconButton>
<MoreIcon />
</IconButton>
<IconButton>
<ChatBubbleOutlineIcon />
</IconButton>
</div>
</div>
<div className="left-search">
<div className='input-container'>
<SearchIcon/>
<input type='text' placeholder='Search...'/>
</div>
</div>
<div className="left-chats">
{
contacts.map( contact =>(
<LeftChats key={contact.id} id = {contact.id} username=
{contact.data.name}/>
))
}
</div>
</div>
);
}
export default LeftBar;
This is my LeftChat Component
import React, { Component } from 'react'
import { Avatar} from "#material-ui/core";
class LeftChats extends Component {
constructor(props) {
super(props)
this.state = {
data : ''
}
}
render()
{
console.log(this.props)
return (
<div className='leftchats'>
<Avatar/>
<div className='chats-info'>
<h2>{this.props.username}</h2>
<p>Some text message...</p>
</div>
</div>
)
}
}
export default LeftChats

Using
<LeftChats key={contact.id} id = {contact.id} username=
{contact.data.name}/>
means that you are passing {contact.data.name} value to your custom LeftChats component. But your data prop is empty. Check your console to see whether you get data or not, since you've used console.log(this.props) inside <LeftChats />.
And for the sake of completness, you should import all from firebase, unless you know exactly what you'are doing:
import * as firebase from 'firebase';
import 'firebase/firestore';
Then have these configuration set appropriately:
const firebaseConfig = {
apiKey: "AIsdfghjzaSyC0ZHz8ooSoi05Vt2X7UL7qV9Aga1o", // use your own
authDomain: "dj-himmels.firebaseapp.com",
databaseURL: "https://dj-himmels.firebaseio.com",
projectId: "dj.himmels",
storageBucket: "dj-himmels.appspot.com",
messagingSenderId: "285437504174",
appId: "1:285566504174:web:0a9ttgb4a968acd08f7ff"
};
which is missing from your code.
In a real react native app for production, you should be using AsyncStorage to proceed your data.
Note: For your next questions try to ask one question at a time, it helps to figure out your real need and answer it accordingly. Right now you seem to be having many issues.
EDITED AFTER THE POSTER COMMENT
1- First of all, you are passing {contact.data.name} to username instead of {contacts.data.name}, because you used React hook useState to define contacts as const [contacts, setContacts] = useState([]) and not as const [contact, setContact] = useState([])
2- Second of all, if at all the
<LeftChats key={contact.id} id = {contact.id} username=
{contact.data.name}/>
component found the {contacts.data.name}, value to pass to username= contacts.data.name}, you will still get empty result because using useState([]) inside const [contacts, setContacts] = useState([]) means that you have set Contact value to empty array.
Nevertheless, you have tried to send value to Contact using React
useEffect hook like so useEffect(() => {
db.collection("contacts").onSnapshot((snapshot)=> setContacts(
snapshot.docs.map((doc)=> ({
id : doc.id,
data : doc.data(),
}))
))
},[]),
which was not triggered before component receives the initial empty array of contact. And your data value fetched from firebase may also be empty, that's why I meant by you are having many issues in the same time.
MY SUGGESTION:
1- Console.log the doc value to check whether it is empty or not. If so, then the problem is in the snapshot.docs.map((doc) otherwise it may be in the empty data inside the
this.state = {
data : ''
}
I make suggestions because I don't really know your real architecture. Also take a look at the react useEffect hooks here You may need to set a conditional value inside the [] of your
useEffect(() => {
},[x])
to reset Contact after that condition. Ultimately remember that there is still a great amount of value in sticking with functional components rather than going back to the class based components. Hope it help !

Related

How to go around a truthy statement here?

I was trying to implement a navbar functionality in my web app which should be switching the information rendered based on a boolean residing in my redux status. Now, when the user is not logged in, the object fetched using my used selector is an empty one which means that is a truthy value hence not enabling me to toggle the element on the navabr as i wish. Is there a way to do that without modifying my redux status ?
Thank you in advance.
import './App.css';
import { Route, Routes, Link, Redirect, Navigate } from "react-router-dom";
import { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import SearchBar from "./components/searchBar";
import Main from './components/main';
import Login from './components/login';
import Register from './components/register';
let linkStyle = { textDecoration: "none", color: "white" };
function App() {
// fetching redux status
let user = useSelector(state => state.loginStatus.user)
let loginStatus = useSelector(state => state.loginStatus.isLoggedIn)
console.log(loginStatus)
const [isActive, setIsActive] = useState(false);
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [avatar, setAvatar] = useState({});
useEffect(() => {
if (loginStatus) {
setIsLoggedIn(loginStatus)
setAvatar(user)
}
}, [loginStatus, isLoggedIn, isActive, avatar])
return (
<div className="App">
<nav className="navbar">
<div className="logo"><Link to={'/'} onClick={() => setIsActive(false)}><img src='https://cdn-icons-png.flaticon.com/512/201/201623.png' /></Link></div>
<h1 className="title">Travel.com</h1>
{
isLoggedIn && (<ul className='menu'>
<li className="link"><Link style={linkStyle} to='/login' onClick={() => setIsActive(true)}>Log-in</Link></li>
<li className="link"><Link style={linkStyle} to='/register' onClick={() => setIsActive(true)}>Register</Link></li>
</ul>)
}
{ /*
isLoggedIn && (
<div className="avatar">
<h6> Hi avatar.user_name!</h6>
</div>
) */
}
</nav>
If I understand your question correctly then a simple solution could be to check if the object is empty using something like: Boolean(Object.keys(logInStatus).length)
this will return false if logInStatus is an empty object, and true if it has any properties. You would pass this as the argument into setIsLoggedIn() in your useEffect().
You said you don't want to change the Redux store, however I would call into question the naming of the isLoggedIn property. By starting a property with is the name implies that it is a boolean value, or at the very least that it will coerse into a boolean value which corresponds to its name. This is something to watch out for as someone who doesn't know the codebase well would most likely end up making an assumption based on conventions, resulting in a situation like this with code behaving the opposite way to how you would expect.

Building first React component/app, issues with axios returning duplicate responses

here is my react component
import './App.css';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faQuoteLeft } from '#fortawesome/free-solid-svg-icons';
import { faTwitter } from '#fortawesome/free-brands-svg-icons';
import React from 'react';
import axios from 'axios';
const quoteIcon = <FontAwesomeIcon icon={faQuoteLeft} />;
const twitterIcon = <FontAwesomeIcon icon={faTwitter} />;
class App extends React.Component {
state = {
quote: ''
};
componentDidMount(){
this.fetchQuote();
}
fetchQuote = () => {
axios.get("https://api.quotable.io/random")
.then((response)=>{
const {quote} = response.data.content;
this.setState({ quote });
})
.catch(error => console.log(error));
}
render() {
const { quote } = this.state;
return (
<div id='wrapper'>
<div id='quote-box'>
<div className='quote-text'>
<div id='quoteIcon'>{quoteIcon}</div><span id='text'>{this.state.quote}</span>
</div>
<div className='quote-author'>-<span id='author'></span></div>
<div className='buttons'>
<a href="link" className='button' id='tweet-quote'>{twitterIcon}</a>
<button className='button' id='new-quote' onClick={this.fetchQuote}>New quote</button>
</div>
</div>
</div>
);
}
}
export default App;
I'm not able to get the quote to render on the screen. I tried to console.log what was happening after I get my response with axios, and for some reason I keep getting two calls. I get two different quotes logged. I'm not sure if that's why I'm having issues with getting it rendered.
I've tried using other methods like fetch and I still get duplicate responses. I'm thinking it's my componentDidMount() mounting multiple times for some reason? but I don't know why it would do that. But I need it to load my function when the component mounts so I don't want to not use it. When I do get rid of it, it obviously doesn't run my function but I believe it still is getting double responses.
The Problem is while you are destructuring inside fetchQuote function
Instead of this >
const {quote}=response.data.current
you can replace it with >
const quote=response.data.current
There is nothing like quote exist in the response.data.current
That's why your quote is not getting set.
your data is:
response.data
{_id: "UBum36vM5", tags: Array(1), content: "Remember that a gesture of friendship, no matter how small, is always appreciated.", author: "H. Jackson Brown Jr.", authorSlug: "h-jackson-brown-jr"…}
But you (Destructuring "quote") what does not exist.
const {quote} = response.data.content; const quote = response.data.content;
While its still logging two quotes for some reason, I think the issue with it not rendering to the screen was how I was setting the state.
I did
this.setState({
quote: response.data.content
});

How does useState set variables? [duplicate]

This question already has answers here:
The useState set method is not reflecting a change immediately
(15 answers)
Closed 1 year ago.
I've posted something similar in the past and never really got an answer and I keep running into this problem. Things in my app are working, but I'm not really sure why. I'm using a third-party api to make a simple recipe app. The data I'm passing into my component is displaying as expected, but it's not logging into the console as expected.
Line 20 of App.js is logging what I'd expect (the data received from the api).
Line 4 of Recipe.jsx is logging what I'd expect (each item in the recipes array).
But line 22 of App.js is coming back as undefined. I would expect that because I setRecipes to 'data', that 'recipes' would log the same as 'data'. Can anyone explain this to me?
import React, { useEffect, useState } from "react";
import "./App.css";
import Recipe from "./Recipe";
import axios from "axios";
function App() {
const APP_ID = "XXXXXXXX";
const APP_KEY = "XXXXXXXXXXXXXXXXXXX";
const url = `https://api.edamam.com/api/recipes/v2?type=public&q=chicken&app_id=${APP_ID}&app_key=${APP_KEY}`;
const [recipes, setRecipes] = useState([]);
useEffect(() => {
getRecipes();
}, []);
const getRecipes = async () => {
const res = await axios(url);
const data = await res.data.hits;
console.log(data);
setRecipes(data);
console.log(recipes);
};
return (
<div className="App">
<form className="search-form">
<input className="search-input" type="text" />
<button className="search-button" type="submit">
Search Recipes
</button>
</form>
{recipes &&
recipes.map((recipe, idX) => <Recipe recipe={recipe} id={idX} />)}
</div>
);
}
export default App;
import React from "react";
const Recipe = ({ recipe, id }) => {
console.log(recipe);
return (
<div key={id}>
<h1>{recipe.recipe.label}</h1>
<p>{recipe.recipe.calories}</p>
<img src={recipe.recipe.images.SMALL.url} alt="" />
</div>
);
};
export default Recipe;
SetState is asynchronous and may not resolve straightaway. See this part of the docs for more information
https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
As you've said, the data coming back is correct, and you've made a call to set state. But the state hasn't resolved by the time you come to the next line where state is consoled out

Can't update state in Context

EDIT BELOW
I'm trying to migrate my states to some contexts so I don't have to constantly prop drill. The problem is, state won't update in the context. I just want a single value in state to be changed to true on click.
Here is my projectTile.js
import React, {useContext} from 'react';
import {ProjectContext, ProjectProvider} from './ProjectContext.js';
import {ProjectFunctionsContext, ProjectFunctionsProvider} from './projectFunctionsContext';
// profpic is going to be pulled from the logged in user, for demonstration purposes I just have it pulling the default profile picture right now
import profpic from '../../icon.png';
// projectImage will be passed from the project component, for now it's just a default chosen from the directory.
import defaultProjectImage from '../../defaultProject.jpg';
const ProjectTile = ({projectAuthorName, projectTitle, projectImage,setSavedProjectData}) => {
const [projects, setProjects] = useContext(ProjectContext);
const [projectClicked, setProjectClicked] = useContext(ProjectFunctionsContext);
// Console Log to try to figure out where any errors are coming from, if they arise.
console.log('Project Author: ' + projectAuthorName + " \n Project Title: " + projectTitle + " \n Project Image: " + projectImage);
// In this return statement, we build the project tile out of the elements that we're getting from the ProjectContext
console.log('projectClicked is doing this: ' + projectClicked);
return (
<div className="ProjectTile__container" onClick={() => {setProjectClicked(true); console.log(projectClicked);setSavedProjectData({projectAuthorName: projectAuthorName})}}>
<img src={defaultProjectImage /*projectImage -- this is commented out because it doesn't work at the moment*/} className="ProjectTile__Img" alt="Placeholder"/>
<div className="ProjectTile__overlay" >
<img src={profpic} className="ProjectTile__icon" alt="Profile"/>
<div className="ProjectTile__text">
{projectTitle}
<br></br>
{projectAuthorName}
</div>
</div>
</div>
);
}
export default ProjectTile;
Here is my projectFunctionsContext.js
import React, {useState, createContext} from 'react';
export const ProjectFunctionsContext = createContext();
export const ProjectFunctionsProvider = (props) => {
const [projectClicked, setProjectClicked] = useState(false);
return(
<ProjectFunctionsContext.Provider
value={[projectClicked,setProjectClicked]}
>
{props.children}
</ProjectFunctionsContext.Provider>
);
}
It just won't update projectClicked to true, what am I doing wrong?
EDIT:
Called the context in parent of this component, making it reset the state.
It happens to work with only one call to get those variables.
You need to set the values object in your ContextProvider which allows you to access the properties in your components with the useContext hook.
Your provider should look like this:
const contextValue = {
projectClicked,
setProjectClicked
};
<ProjectFunctionsContext.Provider value={contextValue}>
{props.children}
</ProjectFunctionsContext.Provider>
Then in your components use the useContext hook to retrieve the values stored in context:
const { projectClicked, setProjectClicked } = useContext(ProjectFunctionsContext);

Learning React: how to useRef in custom component?

I'm learning React and I don't think I understand the concept of useRef properly. Basically, I want to include some tags in tagify input field when a user clicks on a chip that is rendered outside the input box.
My idea is to do something like this (App.js):
import Chip from '#material-ui/core/Chip';
import Tagify from "./Tagify"
...
class App extends React.Component {
...
const { error, isLoaded, quote, tags } = this.state; //tags comes from the server
var tagify = <Tagify tags={tags} />
const addTagOnChipClick = (tag) => {
tagify.addTag(tag)
};
const chips = tags.map(tag => (
<span key={tag.name} className="chips">
<Chip
label={tag.name}
variant="outlined"
onClick={addTagOnChipClick(tag)}
clickable
/>
</span>
))
...
}
The tagify documentation says that
To gain full access to Tagify's (instance) inner methods, A custom ref can be used: <Tags tagifyRef={tagifyRef} ... />
My attempt to gain access to these inner methods was to use useRef (Tagify.js):
import Tags from '#yaireo/tagify/dist/react.tagify'
import '#yaireo/tagify/dist/tagify.css'
export default function Tagify(tags) {
const tagifyRef = useRef()
return (
<Tags
tagifyRef={tagifyRef}
placeholder='Filter by tags...'
whitelist={tags.tags}
/>
)
}
However, tagifyRef.current is undefined. What I'm doing wrong? There's another way to access the inner methods?
Thank you very much!
When are you accessing the ref? Make sure you access the ref only after the component has mounted i.e. in a useEffect:
import Tags from '#yaireo/tagify/dist/react.tagify'
import '#yaireo/tagify/dist/tagify.css'
export default function Tagify(tags) {
const tagifyRef = useRef()
React.useEffect(() => {
console.log(tagifyRef.current)
}, [])
return (
<Tags
tagifyRef={tagifyRef}
placeholder='Filter by tags...'
whitelist={tags.tags}
/>
)
}

Categories

Resources