Hi everyone I am trying to build a chat app but I have a problem
this is my getCollection.js file that contains the request of getting the messages from firebase
import { ref } from "vue"
import {projectfirestore} from "./../../cons"
import { collection ,getDocs,query, orderBy} from "firebase/firestore";
const Doc = ref([])
const error = ref(null)
const getCollection = async (collectionName)=>{
const collectionq = query(collection(projectfirestore,collectionName), orderBy("createdAt"))
await (await getDocs(collectionq)).forEach(doc=>{
doc.data().createdAt && Doc.value.push({...doc.data(),id:doc.id})
})
if (!Doc.value) {
error.value = "could not fetch the data"
Doc.value = null
}
console.log("docs.value",Doc.value)
console.log("error].value",error.value)
return { error, Doc }
}
export default getCollection
and as you see there is two console.log() at the end of the function
so it fetches the data correctly
now this is the ChatWindow.vue
<template>
<div class="chat-window">
<div v-if="error">{{error}}</div>
<div v-if="Doc" class="messages">
<div v-for="doc in Doc" :key="doc.id" class="single">
<span class="created-at">{{doc.createdAt}}</span>
<span class="name">{{doc.name}}</span>
<span class="message">{{doc.message}}</span>
</div>
</div>
</div>
</template>
<script>
import getCollection from '../composables/getCollection.js'
export default {
setup(){
const {error , Doc} = getCollection('message')
console.log("docskk.value",Doc)
console.log("errorkk.value",error)
return {Doc, error}
}
}
</script>
the same here I have two console.log() and this is there values
I would mention that the 2 console.log() in ChatWindow.vue printed before those on the getCollection.js
the expected result is that it must print the message information of the user above the message text field
and I am sure that the component is placed in the View
as it is in this Chatroom.vue
<template>
<div class="container">
<Navbar />
<ChatWindow />
<NewChatRoom />
</div>
</template>
<script>
import Navbar from '#/components/Navbar.vue';
import getUser from '#/composables/getUser';
import { watch } from '#vue/runtime-core';
import { useRouter } from 'vue-router';
import NewChatRoom from '#/components/NewChatRoom.vue';
import ChatWindow from '#/components/ChatWindow.vue';
export default {
components: { Navbar, NewChatRoom, ChatWindow },
setup(){
const {user } = getUser()
const router = useRouter()
watch(user,()=>{
if(!user.value){
router.push({name:"Welcome"})
}
})
}
}
</script>
and when I try to put await before the function call
async setup(){
const {error , Doc} = await getCollection('message')
return {Doc, error}
}
it doesn't show the ChatWindow.vue component on Chatroom.vue view
You should use await when reading asynchronous data.
Use
<script>
import getCollection from '../composables/getCollection.js'
export default {
async setup(){
const {error , Doc} = await getCollection('message')
console.log("docskk.value",Doc)
console.log("errorkk.value",error)
return {Doc, error}
}
}
</script>
then wrap it using defineAsyncComponent
const ChatWindow = defineAsyncComponent(() =>
import('#/components/ChatWindow.vue')
)
read https://vuejs.org/guide/components/async.html#basic-usage
Related
I want to send the login user object to the profile page to edit and upload an image but It ends with the error "ProfileImage.jsx:25 Uncaught TypeError: Cannot read properties of undefined (reading 'photoUrl')"
Here is my Parent Code :
import React from 'react';
import { useState } from 'react';
import {getCurrentUser, logout} from '../../services/authService'
import {useNavigate} from "react-router-dom";
import UpdateForm from '../../components/UpdateForm';
import ProfileImage from '../../components/ProfileImage';
import './Profile.css'
import { useEffect } from 'react';
const ProfilePage = () => {
const navigate = useNavigate();
const [user,setUser] = useState();
useEffect(()=>{
const getUser = async () => {
const {data} = await getCurrentUser();
setUser(data);
}
getUser()
},[])
const handelSignOut =()=>{
logout();
setUser('');
navigate('/sign')
}
return (
<div className='prof-con'>
<div className='prof-flex-wraper'>
<div className='prof-second-row'>
<div className='prof-text'>
<ProfileImage user={user}/>
</div>
<div className='prof-form'>
<UpdateForm user={user}/>
</div>
</div>
</div>
</div>
);
}
export default ProfilePage;
and here is the profile Child component
import React from 'react';
import { useState } from 'react';
import {updatePhoto} from '../services/authService'
import DefaultUserPic from "../images/team-male.jpg";
function ProfileImage({user}) {
const [previewSrc,setPreviewSrc] = useState(null)
const [photoSrc,setPhotoSrc] = useState(null)
const handelPreview = (e)=>{
setPreviewSrc(URL.createObjectURL(e.target.files[0]))
setPhotoSrc(e.target.files[0]);
}
const handelUpload = ()=>{
const formData = new FormData();
console.log(photoSrc)
formData.append("file", photoSrc);
updatePhoto(formData);
}
//console.log(user.photoUrl)
console.log(user)
// if(previewSrc){
// var imagestr=previewSrc;
// console.log(imagestr)
// // imagestr = imagestr.replace("public/", "");
// // var profilePic="http://localhost:3001/"+imagestr;
// }else{
// var profilePic='DefaultUserPic';
// }
return (
<div className='profWraper'>
<input type="file" name="fileToUpload" id="fileToUpload" onChange={handelPreview}/>
<img className='profPhoto' src={previewSrc} alt="No-Image" />
{photoSrc && <button className='uploadBtn' onClick={handelUpload}>Upload</button>}
</div>
);
}
export default ProfileImage;
if I log the user itself I get it in the console but if I try to get any property get an error undefined.
Changing
<ProfileImage user={user}/>
to
{user && <ProfileImage user={user}/>}
might help
You are asynchronously assigning user within the useEffect of ProfileImage. That means user will be undefined until that asynchronously logic resolves. You either have to check to see if user is defined before accessing its properties, or you need to assign user some kind of default properties in your useState.
The error message suggests that user is undefined. Considering that the parent performs an async operation to get the user, I'd say the problem is that the child tries to access the user before the async operation is resolved.
Solution, add a guard against undefined user in the child:
if (user === undefined) return
When I use props to pass an object into the child component, I am unable to use the props inside the setup function (Composition API). I am trying to use the props to run a query from the firebase using the displayName property of the user instead of using 'Abe'. I am new to Vue, please help me out!
<template>
<div v-if="user">
<Process :user="user"/>
</div>
<div v-else>
<h1>Loading user</h1>
</div>
</template>
<script>
import Process from '../views/Process.vue'
import getUser from '#/composables/getUser'
export default {
components: { Process },
setup(){
const { user } = getUser();
return {user}
}
}
</script>
Using user in the setup function gives an undefined error but using it at top of the template returns the correct object.
<template>
<p>Process Payroll</p>
<h1>{{ user.displayName }} </h1>
<h1>{{ docs }} </h1>
</template>
<script>
// import getUser from '#/composables/getUser'
import { ref, onMounted, watch } from 'vue'
import { projectFirestore, projectAuth } from '#/firebase/config'
import { useRouter, useRoute } from 'vue-router'
export default {
props: ['user'],
setup() {
// const { user } = getUser();
// watch(()=> user, () => returnUser())
const lastName = ref("");
const firstName = ref("");
const docs = ref("");
const returnUser = async () => {
const res = await projectFirestore
.collection("users")
.where("displayName", "==", "Abe") // Case sensitive
.get();
const lastNameList = res.docs.map(d => `Abe ${d.data().lastName}`)
console.log(lastNameList)
console.log(user)
console.log(user.displayName)
docs.value = lastNameList;
}
onMounted(returnUser)
return { docs, returnUser};
},
}
</script>
Pass props to setup function:
setup(props) {
....
I´m new to NextJS and React at all so I ask for your forgiveness.
I want to know how to pass an users written text from an input field (inside of Header) into the getStaticProbs function of a specific page via the react context api.
I tried the following source but it doesn`t work - it throws out an error that my way to build leads to an invalid hook call.
Here is my context source:
import React, { createContext, useState } from 'react';
export const SearchContext = createContext();
export const SearchProvider = ({ children }) => {
const [keyword, setKeyword] = useState('');
return (
<SearchContext.Provider
value={{
keyword,
setKeyword,
}}
>
{children}
</SearchContext.Provider>
);
};
to fetch the written string of SearchBar.js:
import React, { useContext, useState } from 'react';
import { useRouter } from 'next/router';
import Image from 'next/image';
import loupe from '../public/images/loupe.png';
import { SearchContext } from '../lib/searchCtx';
const SearchBar = () => {
const search = useContext(SearchContext);
const router = useRouter();
const submitAction = (e) => {
e.preventDefault();
router.push(`/searchResults`);
};
return (
<div className={styles.searchBar}>
<input
type='text'
placeholder='Suche...'
onChange={(e) => search.setKeyword(e.target.value)}
/>
<button className={styles.searchBtn} type='submit' onClick={submitAction}>
<Image src={loupe} alt='' />
</button>
</div>
);
};
export default SearchBar;
and pass it over _app.js:
import Header from '../components/Header';
import Footer from '../components/Footer';
import { SearchProvider } from '../lib/searchCtx';
function MyApp({ Component, pageProps }) {
return (
<>
<SearchProvider>
<Header />
<Component {...pageProps} />
</SearchProvider>
<Footer />
</>
);
}
}
export default MyApp;
to get the value into getStaticProbs of searchResults.js:
import { useEffect, useState, useContext } from 'react';
import { fetchData } from '../lib/utils';
import styles from '../styles/Playlist.module.scss';
import Image from 'next/image';
import { SearchContext } from '../lib/searchCtx';
export default function SearchResults({ videos }) {
console.log(videos);
const sortedVids = videos
.sort((a, b) =>
Number(
new Date(b.snippet.videoPublishedAt) -
Number(new Date(a.snippet.videoPublishedAt))
)
)
return (
<>
<div className={`${styles.playlist_container} ${styles.search}`}>
<h1>Search results</h1>
{sortedVids
.map((vid, id) => {
return (
<div className={styles.clip_container}>
<Image
className={styles.thumbnails}
src={vid.snippet.thumbnails.medium.url}
layout='fill'
objectFit='cover'
alt={vid.snippet.title}
/>
</div>
<div className={styles.details_container}>
<h3>{vid.snippet.title}</h3>
</div>
);
})}
</div>
</>
);
}
export async function getStaticProps() {
const search = useContext(SearchContext);
const { YOUTUBE_KEY } = process.env;
const uploadsURL = `https://youtube.googleapis.com/youtube/v3/search?part=snippet&channelId=UCbqKKcML7P4b4BDhaqdh_DA&maxResults=50&key=${YOUTUBE_KEY}&q=${search.keyword}`;
async function getData() {
const uploadsData = fetchData(uploadsURL);
return {
videos: await uploadsData,
};
}
const { videos } = await getData();
return {
revalidate: 86400,
props: {
videos: videos.items,
},
};
}
Would you help me by 1) telling me the main failure I did and 2) providing me a working source?
How can I achieve it to get the keyword from SearchContext into the uploadsURL (inside of getStaticProbs) or isn`t it possible?
Thanks in advance!!
You can create a dynamic pages under your page folder one called say index.js and one called [slug].js (all under one folder) In the index page you can have your normal search input, when the users submit the query you can do
<a
onClick={() =>
router
.push(`/movies/${search.keyword}`)
.then(() => window.scrollTo(0, 0))}>
search
</a>
and in your [slug].js page you can retrieve that information like so
export async function getServerSideProps(pageContext) {
const pageQuery = pageContext.query.slug;
const apiCall= await fetch(
``https://youtube.googleapis.com/youtube/v3/search?part=snippet&channelId=UCbqKKcML7P4b4BDhaqdh_DA&maxResults=50&key=${YOUTUBE_KEY}&q=${pageQuery}`
);
const results = await apiCall.json();
return {
props: {
data: results,
},
};
}
I don't know if this will work for you but is a solution
I'm using react and firebase and trying to implement signing in with google, but the viewport becomes white probably because the function is not being exported from utils file. Because when I delete it from login.js, the components are displayed
This is my code in utils.js
import firebase from 'firebase/app'
import 'firebase/firestore'
import 'firebase/auth'
import {firebaseConfig} from './config'
firebase.initializeApp(firebaseConfig)
export const auth = firebase.auth();
export const firestore = firebase.firestore();
const GoogleProvider = new firebase.auth.GoogleAuthProvider();
GoogleProvider.setCustomParameters({ prompt: 'select_account' });
export const signInWithGoogle = () => auth.signInWithPopup(GoogleProvider);
export default signInWithGoogle
And this is my Login.js
import React, { Component } from 'react'
import './Login.scss'
import Buttons from '../Buttons'
import { signInWithGoogle } from '../../firebase/utils'
class Login extends Component {
handleSubmit = async e => {
e.preventDefault();
}
render() {
return (
<div className='login'>
<div className='login_wrapper'>
<h3 className='login_heading'>Login</h3>
<form className='login_form' onSubmit={this.handleSubmit}>
<Buttons onClick={signInWithGoogle}>
<div className='button_name'>
Sign in with Google
</div>
</Buttons>
</form>
</div>
</div>
)
}
}
export default Login
I also tried to write like this
let signInWithGoogle;
export default signInWithGoogle = () => auth.signInWithPopup(GoogleProvider)
But the console says that signInWithGoogle is not exported from utils.js
I believe the problem is caused by your signInWithGoogle method returning a Promise object which is blocking your component.
You can either change
export const signInWithGoogle = () => auth.signInWithPopup(GoogleProvider);
to (this isn't recommended, as it makes error handling harder)
export const signInWithGoogle = () => { auth.signInWithPopup(GoogleProvider) };
or change how you use it in your component:
import React, { Component } from "react";
import "./Login.scss";
import Buttons from "../Buttons";
import { signInWithGoogle } from "../../firebase/utils";
class Login extends Component {
handleSubmit = async (e) => {
e.preventDefault();
};
render() {
const onClickSignInWithGoogle = () => {
signInWithGoogle() // doesn't return the Promise
.catch((err) => { // don't forget error handling
console.error("Sign in failed!", err);
alert("Failed to sign in!");
});
}
return (
<div className="login">
<div className="login_wrapper">
<h3 className="login_heading">Login</h3>
<form className="login_form" onSubmit={this.handleSubmit}>
<Buttons onClick={onClickSignInWithGoogle}>
<div className="button_name">Sign in with Google</div>
</Buttons>
</form>
</div>
</div>
);
}
}
export default Login;
I'm using Next.js on a project where the editor component (EditorJS found https://github.com/Jungwoo-An/react-editor-js) can't use SSR. Following the Next docs, I'm trying to dynamically import my Editor component on a page. When I load the page, all components but the Editor component load.
Using the React Components tab in Chrome Dev Tools, it shows me that the Editor component is a LoadableComponent that is stuck on the loading phase.
Editor code (Editor.js)
import React from "react";
import axios from 'axios';
import EDITOR_JS_TOOLS from "./editor-constants"
import EditorJs from "react-editor-js"
// const DynamicComponent = dynamic(() => import("react-editor-js").then((mod) => mod.EditorJs) ,{"ssr" : false})
import { API_LINK, getDoc } from "../services"
/* Document editor for a given forum */
export default function Editor(props) {
/* API call to save current document state */
async function saveDoc() {
const ENDPOINT = API_LINK + "addDoc"
const savedData = instanceRef.current.save();
const DATA = { "link": props.link, "doc": savedData }
const res = await axios.post(ENDPOINT, DATA)
return res.data;
}
const [editorState, setEditorState] = React.useState()
/* On load, get editor document data */
React.useEffect( async () => {
const a = await getDoc(props.link).then((res) => {
setEditorState(res.data)
})
}, [])
const instanceRef = React.useRef(null);
return (
<div>
{/* Save button */}
<button onClick={saveDoc}>Save!</button>
{/* Edtor */}
<EditorJs
instanceRef={instance => (instanceRef.current = instance)}
tools={EDITOR_JS_TOOLS}
i18n={{
messages: {}
}}
data={editorState}
enableReInitialize={true}
/>
</div>
);
};
Page code [id.js]
import dynamic from "next/dynamic";
const DynamicComponent = dynamic(() => import('../../components/Editor'), { ssr: false })
import { ForumComments } from '../../components/ForumComments'
import styles from '../../styles/pages/forum.module.css';
import * as React from 'react'
import { useRouter } from 'next/router'
export default function Forum(props) {
const router = useRouter()
const { id } = router.query
if (id == undefined) {
return (
<p> Loading ... </p>
)
}
else {
return (
<React.Fragment>
<div className={styles.forumTitle}>
<h1>
{ /* props.match.params.title */ }
{id}
</h1>
</div>
<div className={styles.bothSide}>
<div className={styles.leftForum}>
<ForumComments linkProp={id} />
</div>
<div className={styles.rightForum}>
<DynamicComponent key={"document"} link={id} />
</div>
</div>
</React.Fragment>
);
};
};
Here's a screenshot from the Components tab of the relevant component
Screenshot from Components tab showing loading state is null of type Loadable Component