At the moment I have a react app that has multiple pages making different queries from Firebase and more specifically Firestore. These queries are at the top of the file with the code for the component in the return statement.
How do I separate the queries into a separate file so that the query can be called across multiple components without repeating the code for the query? I have a provided a sample page from my website, I would like to separate the query to get the createdBy property and put it into a function called getTicketCreatedBy for example that I can call.
I'm new to React so I'm just trying to understand how best to structure files in React.
Thanks
import React, { useState, useEffect, Fragment } from "react";
import { db } from "../../firebase";
import { updateDoc, doc, getDoc, onSnapshot } from "firebase/firestore";
import {
ArrowPathIcon,
CalendarIcon,
ExclamationCircleIcon,
Bars3Icon,
} from "#heroicons/react/24/outline";
import SelectFieldStandard from "../inputs/SelectFieldStandard";
export default function Details({ currentTicketData, currentTicketRef }) {
const updateTicketState = async () => {
await updateDoc(getUser, {
state: 5,
});
};
//Get CreatedBy property from the Users collection
const [createdByData, setCreatedByData] = useState("");
const createdByDocRef = doc(db, "Users", currentTicketData.createdBy);
useEffect(() => {
onSnapshot(createdByDocRef, (doc) => {
const newData = doc.data();
setCreatedByData(newData);
});
}, []);
return (
<div className="mt-5 md:col-span-1 md:mt-0">
<div className="shadow overflow-hidden rounded-md">
<div className="bg-white dark:bg-gray-800 px-4 py-5 sm:p-6">
<h1 className="mt-2 text-2xl font-bold leading-7 text-gray-900 dark:text-white sm:text-3xl sm:truncate">
Details
</h1>
<div className="pt-2">
<p className="text-sm font-medium text-gray-900 dark:text-white pt-2">
Created By
</p>
<p className="mt-2 flex items-center text-sm text-slate-700 dark:text-slate-400">
<Bars3Icon
className="flex-shrink-0 mr-1.5 h-5 w-5"
aria-hidden="true"
/>
{createdByData.firstName} {createdByData.lastName}
</p>
</div>
</div>
</div>
</div>
);
}
I can't seem to find any information online for how to separate Firebase queries.
Related
In the redux toolkit on the eCommerce website, When I add the product to the cart total price changes from 0 to null. I have been struggling with this problem.Instead of null it should show the total cart price by adding product prices
This is the Cart slice of the e-commerce website in redux-toolkit
import { createSlice } from "#reduxjs/toolkit";
const cartSlice = createSlice({
name: "cart",
initialState: {
products: [],
quantity: 0,
total: 0,
},
reducers: {
addProduct: (state, action) => {
state.products.push(action.payload);
state.quantity += 1;
state.total += action.payload.price * action.payload.quantity;
},
removeProduct: (state, action) => {
let index = state.products.indexOf(action.payload)
state.quantity -= action.payload
state.products.splice(index, 1)
}
},
});
export const { addProduct ,removeProduct} = cartSlice.actions;
export default cartSlice.reducer;
This is the Product page of an E-commerce website. Last function const addtocart=()=>{dispatch(addProduct({...product,quantity}))} add product to cart.
import React, { useEffect, useState } from 'react'
import { Link, useParams } from 'react-router-dom'
import {useDispatch, useSelector} from "react-redux"
import { addProduct } from '../ReduxToolkit/features/cartSlice'
const ProductPage = () => {
const quantity = useSelector((state) => state.cart.quantity)
const dispatch=useDispatch()
const { id } = useParams("")
const [product, setProduct] = useState([])
useEffect(() => {
const getData = async () => {
const res = await fetch(`/products/oneproduct/${id}`, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
})
const data = await res.json()
if (res.status !== 200) {
console.log("no data present")
} else {
setProduct(data)
}
}
getData()
}, [id])
const addtocart=()=>{dispatch(addProduct({...product,quantity}))}
return (
<div>
<section className="text-gray-600 body-font font-abc overflow-hidden">
<div className="container px-5 py-16 mx-auto">
<div className="lg:w-4/5 mx-auto flex align-middle justify-center flex-wrap">
<img alt="ecommerce" className="lg:w-1/3 w-[230px] object-contain lg:h-96 md:h-76 rounded" src={product.oneProduct?.img} />
<div className="lg:w-1/2 w-full lg:pl-10 lg:py-6 mt-6 lg:mt-0">
<h2 className="text-sm title-font font-semibold text-gray-500 my-2 tracking-widest">{product.oneProduct?.cat}</h2>
<h1 className="text-gray-900 text-3xl my-4 title-font font-bold mb-1">{product.oneProduct?.title}</h1>
<p className="leading-relaxed">{product.oneProduct?.desc}</p>
<div className="flex mt-6 items-center pb-5 border-b-2 border-gray-100 mb-5">
<div className="flex ml-6 items-center">
</div>
</div>
<div className="flex">
<span className="title-font font-medium text-2xl text-gray-900">$ {product.oneProduct?.price}</span>
<button className=" cart flex ml-4 text-white bg-indigo-500 border-0 py-2 px-4 focus:outline-none lg:w-32 hover:bg-indigo-600 rounded md:w-4 md:px-6 text-sm" onClick={addtocart}>Add to cart</button>
<Link to="/cart">
<button className=" cart flex ml-4 text-white bg-indigo-500 border-0 py-2 px-4 focus:outline-none lg:w-32 hover:bg-indigo-600 rounded md:w-4 md:px-6 text-sm">Order page
</button>
</Link>
</div>
</div>
</div>
</div>
</section>
</div>
)
}
export default ProductPage
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
Check your getting data from the fetch call
Check your local state against your redux state
See your call stack and Redux dev tools
Hard code state values until you can get it to work
-Keep in mind redux will send back 200 response on every fetch call even the ones that fail, use originalStatus instead of status for your response check
My first indication of why you are getting null starts with the optional chaining pointing right at oneProduct saying it does not exist and/or your price is null on this line
{product.oneProduct?.price} is it possible that oneProduct does not exist yet on first render? since it's reading the useState local values and not the redux state?
Here's an example of how I would get the total price, and you could use this concept to get the singular price of a product as well
I would put this in cartSlice export const selectCurrentTotal = (state) => state.total;
Then in Product page import the selectCurrentTotal like this
import { selectCurrentTotal } from '../features/cart/cartSlice';
and use it like this const total = useSelector(selectCurrentTotal);
Now we can replace the {product.oneProduct?.price} with total
You should use the useSelector to grab the products out of the redux state just like you did with quantity. Try hard coding oneProduct and see if this is the issue. I would not store any state values needed in redux in local useState just my own preference.
I would suggest removing the useEffect, your cartService.js should be handling all the fetch calls (builder mutations) and then your reducers in cartSlice handling the actions and state updates. A fetch call should have trycatch to help keep it clean and error free.
https://redux-toolkit.js.org/usage/usage-guide#asynchronous-logic-and-data-fetching
What does your call stack say? and does Redux Dev Tools show the price getting updated at all or any products added to your state?
I've got a static site and I'm trying to render a simple list of items retrieved from Supabase. I had this working in React 17, but with React 18 it throws this error sporadically, and the fallback doesn't reliably appear. This seems to mostly happen when doing a hard page refresh. Authentication is done via cookie and server-side middleware.
Error: This Suspense boundary received an update before it finished hydrating. This caused the boundary to switch to client rendering. The usual way to fix this is to wrap the original update in startTransition.
//index.tsx (page component)
import { AddEventButton } from '#/components/index';
import { ComponentWithLayout } from '#/types/definitions';
import { Suspense } from 'react';
import { getNavbarLayout } from '#/layouts/NavbarLayout';
import UpcomingEventListSkeleton from '#/components/upcomingEventList/UpcomingEventList.skeleton';
import dynamic from 'next/dynamic';
const UpcomingEventList = dynamic(
() => import('#/components/upcomingEventList/UpcomingEventList'),
{ suspense: true }
);
const UpcomingEventsPage: ComponentWithLayout = () => {
return (
<div className="mx-auto lg:w-1/2 xs:w-full">
<div className="space-y-2">
<Suspense fallback={<UpcomingEventListSkeleton />}>
<UpcomingEventList />
</Suspense>
</div>
<AddEventButton />
</div>
);
};
UpcomingEventsPage.getLayout = getNavbarLayout;
export default UpcomingEventsPage;
//UpcomingEventList.tsx
import { CalendarEvent } from '#/types/definitions';
import { CalendarEventConfiguration } from '#/utils/appConfig';
import { UpcomingEvent } from './components/upcomingEvent/UpcomingEvent';
import { supabaseClient } from '#supabase/auth-helpers-nextjs';
import React from 'react';
import dayjs from 'dayjs';
import useSWR from 'swr';
/**
* Shows a list of Upcoming Event components
* #returns UpcomingEventList component
*/
export const UpcomingEventList = () => {
const { data } = useSWR(
'upcomingEvents',
async () =>
await supabaseClient
.from<CalendarEvent>(CalendarEventConfiguration.database.tableName)
.select('*, owner: user_profile(full_name, avatar)')
.limit(10)
.order('end', { ascending: true })
.gt('end', dayjs(new Date()).toISOString()),
{
refreshInterval: 3000,
suspense: true,
}
);
return (
<>
{data && data.data?.length === 0 && (
<div className="card shadow bg-base-100 mx-auto w-full border text-center p-5">
<div className="text-base-content font-medium">
No upcoming events scheduled
</div>
<div className="text-sm opacity-50">
Events can be scheduled by clicking the purple button in the
bottom-right corner.
</div>
</div>
)}
{data &&
data.data?.map(
({ id, owner, start, end, number_of_guests, privacy_requested }) => (
<UpcomingEvent
key={id}
title={owner?.full_name}
startDate={start}
endDate={end}
numberOfGuests={number_of_guests}
privacyRequested={privacy_requested}
avatarSrc={owner?.avatar}
/>
)
)}
</>
);
};
export default UpcomingEventList;
//UpcomingEventListSkeleton.tsx
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import {
faEye,
faPlayCircle,
faStopCircle,
faUser,
} from '#fortawesome/free-solid-svg-icons';
import React from 'react';
const UpcomingEventListSkeleton = () => (
<>
{[...Array(5).keys()].map((v, i) => (
<UpcomingEventSkeleton key={i} />
))}
</>
);
export default UpcomingEventListSkeleton;
const UpcomingEventSkeleton = () => (
<div className="card shadow bg-base-100 text-base-content font-medium mx-auto w-full border">
<div className="flex items-center m-2 animate-pulse">
<div className="mr-5 ml-1">
<div className="rounded-full bg-gray-200 h-12 w-12"></div>
</div>
<span className="space-y-1">
<div className="text-xl">
<div className="h-8 bg-gray-200 rounded w-56"></div>
</div>
<div
className="text-sm opacity-50"
style={{
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
width: '20em',
}}
>
<div className="grid grid-cols-10 grid-rows-4">
<div>
<FontAwesomeIcon icon={faPlayCircle} className="col-span-1" />
</div>
<div className="h-2 bg-gray-200 rounded col-span-9 mt-1 w-36"></div>
<div>
<FontAwesomeIcon icon={faStopCircle} />
</div>
<div className="h-2 bg-gray-200 rounded col-span-9 mt-1 w-36"></div>
<div>
<FontAwesomeIcon icon={faUser} />
</div>
<div className="h-2 bg-gray-200 rounded col-span-9 mt-1 w-36"></div>
<div>
<FontAwesomeIcon icon={faEye} />
</div>
<div className="h-2 bg-gray-200 rounded col-span-9 mt-1 w-36"></div>
</div>
</div>
</span>
</div>
</div>
);
I've tried swapping the components around, storing the results via useEffect to force client-side rendering, and nothing seems to make this error go away or even change behavior. A simple example that removes the custom components and replaces them with simple strings suffers the exact same issue.
I don't understand how useTransition would help me here, nor how I would even make use of it with useSWR as my fetching mechanism. I've searched around and most people who have this issue seem to be using SSR, and not SSG as I am.
Any help is appreciated.
The cause of my issue was useSwr was making calls before the #supabase/auth-helpers-nextjs user object had fully initialized. This was causing multiple requests to be fired, some authorized, others not, and was making the data object flip rapidly between multiple null | hydrated states. Once I added a conditional to useSwr, it stopped the calls and Suspense began working correctly.
import { CalendarEvent } from '#/types/definitions';
import { CalendarEventConfiguration } from '#/utils/appConfig';
import { UpcomingEvent } from './components/upcomingEvent/UpcomingEvent';
import { supabaseClient } from '#supabase/auth-helpers-nextjs';
import { useUser } from '#supabase/auth-helpers-react';
import React from 'react';
import dayjs from 'dayjs';
import useSWR from 'swr';
/**
* Shows a list of Upcoming Event components
* #returns UpcomingEventList component
*/
export const UpcomingEventList = () => {
const user = useUser(); // <--- get user via hook
const { data } = useSWR(
user && !user.isLoading ? 'upcomingEventsList' : null, // <--- make SWR conditional on user being available
async () =>
await supabaseClient
.from<CalendarEvent>(CalendarEventConfiguration.database.tableName)
.select('*, owner: user_profile(full_name, avatar)')
.limit(10)
.order('end', { ascending: true })
.gt('end', dayjs(new Date()).toISOString()),
{ refreshInterval: 10000, suspense: true }
);
return (
<>
{data?.data && !data.data.length && (
<div className="card shadow bg-base-100 mx-auto w-full border text-center p-5">
<div className="text-base-content font-medium">
No upcoming events scheduled
</div>
<div className="text-sm opacity-50">
Events can be scheduled by clicking the purple button in the
bottom-right corner.
</div>
</div>
)}
{data?.data?.map(
({ id, owner, start, end, number_of_guests, privacy_requested }) => (
<UpcomingEvent
key={id}
title={owner?.full_name}
startDate={start}
endDate={end}
numberOfGuests={number_of_guests}
privacyRequested={privacy_requested}
avatarSrc={owner?.avatar}
/>
)
)}
</>
);
};
export default UpcomingEventList;
This worked in my case, but I'd wager that other encountering this error may have similar request patterns that cause Suspense to perform poorly, or break altogether.
SWR was somewhat masking the issue, and by removing and testing the calls with useState and useEffect, I was able to narrow down the problem to the SWR call, and finally the missing user object.
So I have a page in react that is constantly rendering , say for eg. when I console log I can see that getting logged infinitely in a loop. is it some hook on some inner components that is getting rendered constantly I can't figure out
When i comment out
<CartContainer line_items = {line_items} />
it still doesnt solve the issue.
could useCartState() context be the issue?
Below is the page
import React, {useEffect} from "react"
import { CartContainer, NavigationBar } from "../components"
import {useRouter} from 'next/router'
import { useCartState } from "../context/Cart"
import { QuestionMarkCircleIcon } from '#heroicons/react/solid'
const getPaymentAPIStatus = async () =>
{
const response = await fetch("https://api.nowpayments.io/v1/status")
const data = await response.json()
}
const CartPage = () => {
const router = useRouter();
const {line_items, subtotal, total_items} = useCartState() // to get cart details --anaya
const isEmpty = line_items.length === 0
if(isEmpty)
return(
<div>
<NavigationBar/>
<p>Your cart is empty. Please add some items</p>
</div>
)
else {
return(
<div>
<NavigationBar/>
<div className="bg-white">
<div className="max-w-2xl mx-auto pt-16 pb-24 px-4 sm:px-6 lg:max-w-7xl lg:px-8">
<h1 className="text-3xl font-extrabold tracking-tight text-gray-900 sm:text-4xl">Shopping Cart</h1>
<form className="mt-12 lg:grid lg:grid-cols-12 lg:gap-x-12 lg:items-start xl:gap-x-16">
<section aria-labelledby="cart-heading" className="lg:col-span-7">
<h2 id="cart-heading" className="sr-only">
Items in your shopping cart
</h2>
<CartContainer line_items = {line_items} />
</section>
{/* Order summary */}
<section
aria-labelledby="summary-heading"
className="mt-16 bg-gray-50 rounded-lg px-4 py-6 sm:p-6 lg:p-8 lg:mt-0 lg:col-span-5"
>
<h2 id="summary-heading" className="text-lg font-medium text-gray-900">
Order summary
</h2>
<dl className="mt-6 space-y-4">
<div className="flex items-center justify-between">
<dt className="text-sm text-gray-600">Subtotal</dt>
<dd className="text-sm font-medium text-gray-900">{subtotal.formatted_with_symbol}</dd>
</div>
<div className="border-t border-gray-200 pt-4 flex items-center justify-between">
<dt className="flex items-center text-sm text-gray-600">
<span>Shipping estimate</span>
<a href="#" className="ml-2 flex-shrink-0 text-gray-400 hover:text-gray-500">
<span className="sr-only">Learn more about how shipping is calculated</span>
<QuestionMarkCircleIcon className="h-5 w-5" aria-hidden="true" />
</a>
</dt>
<dd className="text-sm font-medium text-gray-900">Free</dd>
</div>
<div className="border-t border-gray-200 pt-4 flex items-center justify-between">
<dt className="text-base font-medium text-gray-900">Order total</dt>
<dd className="text-base font-medium text-gray-900">{subtotal.formatted_with_symbol}</dd>
</div>
</dl>
<div className="mt-6">
<button
type="submit"
className="w-full bg-indigo-600 border border-transparent rounded-md shadow-sm py-3 px-4 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-50 focus:ring-indigo-500"
>
Checkout
</button>
</div>
<div className="mt-6 text-sm text-center">
<p>
or{' '}
<a href ={`${router.basePath}/ChooseProduct`} className="text-indigo-600 font-medium hover:text-indigo-500">
Continue Designing<span aria-hidden="true"> →</span>
</a>
</p>
</div>
</section>
</form>
</div>
</div>
</div>
)
}
}
export default CartPage
How do i solve this?
Thanks in advance!
Adding useCartState as requested
import {createContext, useEffect, useContext, useReducer} from 'react'
import {commerce} from '../../lib/commerce'
//need to correct this file to be a tsx file in the future
//Provides a context for Cart to be used in every page
const CartStateContext = createContext()
const CartDispatchContext = createContext()
const SET_CART = "SET_CART"
const initialState = {
total_items: 0,
total_unique_items: 0,
subtotal:[],
line_items: [{}]
}
const reducer = (state,action) => {
switch(action.type){
case SET_CART:
return { ...state, ...action.payload }
default:
throw new Error(`Unknown action: ${action.type}` )
}
}
export const CartProvider = ({children}) => {
const [state, dispatch] = useReducer(reducer, initialState)
const setCart = (payload) => dispatch({type: SET_CART, payload})
useEffect(() => {
getCart()
},[state]) // Getting the page to be rendered whenever the cart objects are changed
const getCart = async() => {
try {
const cart = await commerce.cart.retrieve()
setCart(cart)
} catch (error){
console.log("error")
}
}
return (
<CartDispatchContext.Provider value = {{setCart}}>
<CartStateContext.Provider value = {state}>
{children}
</CartStateContext.Provider>
</CartDispatchContext.Provider>
)
}
export const useCartState = () => useContext (CartStateContext)
export const useCartDispatch = () => useContext (CartDispatchContext)
The problem here is that you have a useEffect with state as a dependency, meaning that every time the state value is altered, the getCart function gets called.
Meanwhile, getCart sets the state within it.
Therefor, it creates a cycle where getCart sets the state, the useEffect callback runs as a result, getCart gets called again, sets the state again, and so on.
The bottom line is that you can not set the state inside of a function and call that function every time the state value is altered.
I'm working on a toasts notifications system using React v17 and the React context API. I'm NOT using Redux.
The problem:
Toasts are dismissed automatically after a given delay. The toast element which is dismissed is removed from the list from the context. The problem is that each Toast component is re-render, the whole list is re-render, each time the list change.
I don't want that each component be re-rendered. Only the dismissed Toast component should be "re-render", understand deleted from the list displayed.
I put key attribute on my Toast components but it doesn't work as I expected it would.
Thank you for helping me !
The code below:
function Layout() {
const toastsContext = useContext(ToastsContext);
const toastsList = toastsContext.toastsList;
const [list, setList] = useState([]);
useEffect(() => {
setList(toastsList);
}, [toastsList]);
const displayToasts = list.map(toast =>
<Toast
key={toast.id.toString()}
id={toast.id}
color={toast.color}
title={toast.title}
message={toast.message}
dismissable={toast.dismissable}
showTime={toast.showTime}
autoDismissDelay={toast.autoDismissDelay}
redirectTo={toast.redirectTo} />
);
return(
<div className='bg-slate-800 text-slate-400 min-h-screen relative'>
<Header />
<Outlet />
<div className='fixed top-20 right-4 flex flex-col gap-2'>
{displayToasts}
</div>
</div>
);
}
export default Layout;
Toast component
import { memo, useCallback, useContext, useEffect, useState } from "react";
import { ToastsContext } from "../context/ToastsContext";
import { FiX, FiArrowRight } from 'react-icons/fi';
import { Link } from "react-router-dom";
function Toast({id, color, title, message, dismissable, autoDismissDelay, showTime, redirectTo}) {
const toastsContext = useContext(ToastsContext);
const [hiddenToast, setHiddenToast] = useState(false);
const getToastColor = () => {
switch (color) {
case 'primary':
return 'bg-sky-500';
case 'danger':
return 'bg-rose-500';
case 'success':
return 'bg-green-500';
default:
return 'bg-sky-500';
}
};
const dismissToast = useCallback(() => {
setHiddenToast(true);
setTimeout(() => {
document.getElementById(`toast${id}`).className = 'hidden';
toastsContext.dismissToast(id);
}, 310);
}, [id, toastsContext]);
useEffect(() => {
const interval = setInterval(() => {
dismissToast();
}, autoDismissDelay);
return () => {
clearInterval(interval);
}
}, [autoDismissDelay, dismissToast]);
return(
<div id={'toast'+id} className={`transition ease-out duration-300 text-slate-50 text-sm rounded-lg drop-shadow-lg opacity-100 ${getToastColor()} ${hiddenToast ? 'translate-x-20 opacity-0' : ''}`}>
<div className="w-72">
<div className={`${(redirectTo && redirectTo !== '') || (message && message !== '') ? 'py-2' : 'py-4'} px-4 flex font-semibold items-center`}>
<p>{title}</p>
<div className="ml-auto flex items-center">
{showTime ? <p className="text-xs mr-3">11m ago</p> : null}
{dismissable ?
<button type="button" className="flex items-center justify-center text-base" onClick={dismissToast}>
<FiX />
</button>
: null
}
</div>
</div>
{
redirectTo && redirectTo !== '' ?
<Link className={`border-t border-slate-700 bg-slate-800 rounded-b-lg px-4 py-3 font-medium hover:bg-slate-700 block`} to={redirectTo}>
<p className="flex items-center justify-between">
<span>{message ? message : 'See more'}</span>
<FiArrowRight />
</p>
</Link>
:
message ?
<div className={`border-t border-slate-700 bg-slate-800 rounded-b-lg px-4 py-3 font-medium`}>
<p className="flex items-center justify-between">
<span>{message}</span>
</p>
</div>
: null
}
</div>
</div>
);
};
export default memo(Toast);
That's the default behavior in react: When a component (eg, Layout) re renders, so to do all its children (the Toasts). If you want to skip rendering some of the toasts, then Toast will need to use React.memo. Also, for the memoization to work, the props to each Toast will need to stay the same from one render to the next. From looking at your code i think that will happen without any changes, but it's important to know so you don't think memo is enough on its own.
import { memo } from 'react';
function Toast() {
// ...
}
export default memo(Toast);
Well Im Trying To Basically Use Speech To Text With React-Speech-Recognition Package, I Tried To Make a Button Icon Which Stops onClick When The Mic Was Off or Turns on When Earlier it Was off
I Tried a Lot To Fix the Issue,
here's My Code.
import React, { Component, useRef } from "react";
import firebase from "../firebase";
import { createSpeechlySpeechRecognition } from "#speechly/speech-recognition-polyfill";
import SpeechRecognition, {
useSpeechRecognition,
} from "react-speech-recognition";
var axios = require("axios").default;
export default function Chat() {
let messages = [{ main: "Hey there! Wassop", class: "left" }];
let messageRef = useRef();
const appId = "I_HAVE_MY_KEY_HERE";
const SpeechlySpeechRecognition = createSpeechlySpeechRecognition(appId);
SpeechRecognition.applyPolyfill(SpeechlySpeechRecognition);
const {
transcript,
listening,
browserSupportsSpeechRecognition,
isMicrophoneAvailable,
} = useSpeechRecognition();
const startListening = () =>
SpeechRecognition.startListening({ continuous: true });
function Mic() {
if (listening) {
return SpeechRecognition.stopListening()
} else {
return SpeechRecognition.abortListening()
}
}
function onSend(e) {
let message = messageRef.current.value;
e.preventDefault();
document.getElementById("messages").innerHTML +=
"<div class='right'>" + message + "</div>";
if (message == "") {
alert("Field shall not be empty!");
}
var options = {
---API REQUEST----
};
axios
.request(options)
.then(function (response) {
messageRef.current.value = "";
messages.push({
main: response.data.cnt,
class: "left",
});
document.getElementById("messages").innerHTML +=
"<div class='left'>" + response.data.cnt + "</div>";
console.log(messages);
})
.catch(function (error) {
console.error(error);
});
}
return (
<div className="card">
<h1 className="text-2xl text-center font-bold">Chat Box</h1>
<p>Microphone: {listening ? "on" : "off"}</p>
<p>{transcript}</p>
<br />
<div id="messages">
{messages.map((message) => (
<div key={message.main} className={message.class}>
{message.main}
</div>
))}
</div>
<br />
<form onSubmit={onSend}>
<div class="relative flex w-2/4 flex-wrap items-stretch mb-3">
<span class="z-10 h-full leading-snug font-normal absolute text-center text-blueGray-300 absolute bg-transparent rounded text-base items-center justify-center w-8 pl-3 py-3">
<img src="https://img.icons8.com/external-flatart-icons-flat-flatarticons/64/000000/external-message-contact-flatart-icons-flat-flatarticons.png" />
</span>
<input
type="text"
ref={messageRef}
placeholder="Message"
class="px-3 py-3 placeholder-blueGray-300 text-blueGray-600 relative bg-white bg-white rounded text-sm border-0 shadow outline-none focus:outline-none focus:ring w-full pl-10"
/>
<span class="z-10 h-full leading-snug font-normal absolute text-center text-blueGray-300 absolute bg-transparent rounded text-base items-center justify-center w-8 right-0 pr-3 py-3 mic">
<img
src="https://img.icons8.com/material-rounded/24/000000/microphone.png"
onClick={Mic()}
/>
</span>
</div>
<button
type="submit"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
>
Button
</button>
</form>
</div>
);
}
I Get The Error Of Too Many Renders Which Im Not Able To Figure Out How To Fix I Tried Loooking a lot But Nothing Helped, Im Not Even Able To Understand Someone Please Help Me.
Reason for too much re-render
If you are binding your onClick functions like this:
onClick={Mic()}
React will call the function, without the need of an actual click. You should use onClick={Mic} or onClick={() => Mic()}
There are a couple of other issues with your code:
1. Using ref
You are using a useRef variable to hold the messageuseRef update will not trigger a re-render. Instead try to use a useState hook to bind the variable to a state variable
2. Creating the object inside the functional component
You are creating messages object inside the functional component. On every re-render, this messages object will be initialized to the same array. Bind this to a state variable to reflect changes made to the messages object.
import {useState} from 'react'
const [messages, setMessages] = useState([{ main: "Hey there! Wassop", class: "left" }])
3. DOM Manipulation
You should avoid DOM manipulation like:
document.getElementById("messages").innerHTML += "<div class='right'>" + message + "</div>";
if possible. You can use refs in this case and bind it to the JSX.
const messageRef = useRef('')
return(
...
<div id="message" ref={messageRef}> ...
...
)
I will strongly suggest reading more about React lifecycle and hooks:
Docs