Keep catching same value within map() method recurring - reactjs

In my project, I have a component for rendering all the records from a table in the database. I'm using map() method to populate the records to the page. The components with values were displayed all right on the page. However, in the console, the console.log result is recurring and will never stop.
Here is my code in the component which using map(). CourseCard.js
import {FontAwesomeIcon} from "#fortawesome/react-fontawesome";
import fontawesome from "#fortawesome/fontawesome";
import {faGraduationCap, faArrowAltCircleRight, faUser} from '#fortawesome/fontawesome-free-solid'
import {useNavigate, useParams} from "react-router-dom";
fontawesome.library.add(faGraduationCap, faArrowAltCircleRight, faUser);
function CourseCard({courses}) {
let navigate = useNavigate();
const routeCourseDetail = (id) => {
return () => {
let path = `/courses/detail/${id}`;
navigate(path);
}
}
return (
<div>
{courses.map((course) => {
return <div className="col-sm col-md col-lg">
<div className="card blue" key={course.id} onClick={routeCourseDetail(course.id)}>
<div className="inner">
<h1 style={{color: "white"}}>{course.name}</h1>
<h2 style={{color: "white", marginTop: "-0.5em"}}>{course.level}</h2>
</div>
<div className="icon" style={{color: "white"}}>
{/*<FontAwesomeIcon icon="fa-user" />*/}
<FontAwesomeIcon icon="graduation-cap"/>
</div>
<div className="footer">
More info <FontAwesomeIcon icon="arrow-alt-circle-right"/>
</div>
</div>
</div>
})}
</div>
)
}
export default CourseCard;
And the code for making the component formed in a matrix. CourseMatric.js
import React, {useEffect, useState} from "react";
import CourseCard from "./CourseCard";
import {useNavigate} from "react-router-dom";
import axios from "axios";
function CourseMatrix() {
let navigate = useNavigate();
const routeAdd = () => {
let path = "./new";
navigate(path);
}
const [coursesList, setCoursesList] = useState([]);
useEffect(() => {
axios.get(`http://localhost:3001/courses`).then((response) => {
setCoursesList(response.data);
console.log(response.data);
})
});
return (
<div className="main_content grid-2">
<div className="wrapper">
<CourseCard courses={coursesList}/>
</div>
<div className="new-line">
<button className="green_bt option_list round mr" onClick={routeAdd}>Add
</button>
</div>
</div>
)
}
export default CourseMatrix;
Anyone helps me to figure out which part is not correct to cause this problem to occur? Many thanks.

You need to add dependency in useEffect :
useEffect(() => {
axios.get(`http://localhost:3001/courses`).then((response) => {
setCoursesList(response.data);
console.log(response.data);
})
},[]);
useEffect asks for dependency so that it executes the effect only when the value is changed. The empty [] dependency will only execute the effect once.
Since you didn't add any dependency , the effect was getting executed on every re-render causing infinite loop (setCoursesList(response.data) was causing re-render) .

Related

Uncaught Error: Rendered more hooks than during the previous render when trying to have a nested loop

I know same question probably asked multiple times. But I couldn't find the answer I'm looking for.
This is the code for Task:
import Navbar from './Navbar';
import "./Idea.css";
import GetData from '../restApiMethods/GetData';
import React from "react";
function Task() {
const ids = GetData("ideas/id");
return (
<div>
<Navbar />
<div className="idea-design">
<div className="container">
{
ids.map((id,index) => {
return (
<div key={index}>
{
GetData(`ideas/${id}`).map((task,index) => {
return(
<div key={index} className="row border border-secondary">
<div className="col">
<div>
<p>{task.taskDescription}</p>
</div>
</div>
</div>
)
})
}
</div>
)
})
}
</div>
</div>
</div>
)
}
export default Task
Getdata dunction:
import axios from "axios";
import {useState, useEffect} from "react";
function GetData(data) {
const [datas, setDatas] = useState([]);
useEffect(() =>{
const fetchData = () => {
axios.get(`http://localhost:8080/api/${data}`).then(res =>{
console.log(res);
setDatas(res.data);
});
};
fetchData();
}, []);
return datas;
}
export default GetData
If someone can give me some idea why I'm getting this error: Rendered more hooks than during the previous render, would be really helpful.
GetData actually is a custom hook because it's a function that calls hooks. Therefore subject to the rules of hooks.
It should be called useGetData -- I'll refer to it as that for this answer. You can't call it in a loop, as when the ids array changes length, the number of calls to useGetData will change in the parent component Task. This isn't allowed in React because hooks are supposed to be in a predictable order and never change -- it's a declarative model.
To fix this, break out a new component called Task (rename your current one to Tasks or whatever makes sense for you) and call it once in there. This doesn't break the rules of hooks as it is only within a component that the number of calls can't change between renders.
Tasks
import Navbar from "./Navbar";
import "./Idea.css";
import useGetData from "../restApiMethods/useGetData";
import React from "react";
import Task from "./Task";
function Tasks() {
const ids = useGetData("ideas/id");
return (
<div>
<Navbar />
<div className="idea-design">
<div className="container">
{ids.map((id, index) => {
return <Task id={id} key={id} />;
})}
</div>
</div>
</div>
);
}
export default Tasks;
Task
export default function Task({ id }) {
const data = useGetData(`ideas/${id}`);
return (
<div>
{data.map((task, index) => {
return (
<div key={index} className="row border border-secondary">
<div className="col">
<div>
<p>{task.taskDescription}</p>
</div>
</div>
</div>
);
})}
</div>
);
}
import axios from "axios";
import { useState, useEffect } from "react";
function useGetData(data) {
const [datas, setDatas] = useState([]);
useEffect(() => {
const fetchData = () => {
axios.get(`http://localhost:8080/api/${data}`).then((res) => {
console.log(res);
setDatas(res.data);
});
};
fetchData();
}, []);
return data;
}
export default useGetData;

Displaying audio waveforms in React

So i´m building this webpage which allow users to upload a song, and the displayind that sound as a card on the home-page. Sort of like Soundcloud...
Im just getting to learn React, after coming from html, css and JS. So please understand im new to this all.
I´ve been researched the topic alot, and no one has seemed to work for me.
Ive been trying howler.js, and wavesurfer.js, without any luck of displaying waveforms.
have anyone else tried doing this before? someone who could maybe help out?
import { ErrorResponse } from '#remix-run/router';
import React from 'react'
import wavesurfer from 'wavesurfer.js'
import "./css/audio.css"
import { useRef } from 'react';
export const AudioVisualizer = (props) => {
// the homepage has a function to map through all the objects in the
// database, and in return i get every object. I then get the link from each
// object and pass this link into this function as an ARgument.
let link = props;
const audioRef = useRef();
console.log("here is props: " + link);
try {
var audioTrack = wavesurfer.create({
container: audioRef,
wavecolor: "#eee",
progressColor: "red",
barWidth: 2,
});
audioTrack.load(link);
} catch (ErrorResponse) {
console.error("Something happened..");
return ErrorResponse;
};
return (
<div className='audio' ref={audioRef}>
</div>
)
}
From there I have the actual Home.js page where I want to display the returned from the function above.
the home.js file looks like this:
import React, { useEffect, useState } from 'react';
import '../components/css/home/home.css';
import {collection, getDocs, onSnapshot} from 'firebase/firestore';
import {db} from '../firebase'
import { useNavigate } from 'react-router-dom';
import {ClipLoader} from 'react-spinners';
import {AudioVisualizer} from "../components/audioVisualizer"
const Home = () => {
const [songs, setSongs] = useState([]);
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
useEffect(() => {
setLoading(true);
const retrieveSongs = onSnapshot(
collection(db, "songs"),
(snapshot) => {
let arrayList = [];
snapshot.docs.forEach((doc) => {
arrayList.push({ id: doc.id, ...doc.data() });
});
setSongs(arrayList);
setLoading(false);
},
(error) => {
console.log(error);
}
);
return () => {
retrieveSongs();
};
}, []);
return (
<div className='home_wrapper'>
<>
{loading ?
<ClipLoader color="#36d7b7" />
:
<div className='homepage_container'>
{ songs.map((data) => {
return (
<article key={data.id} className='card'>
<div className='card_content'>
<img className='card_image' src={data.image} />
<div className='song_info'>
<h2>{data.title}</h2>
<h4>{data.artist}</h4>
</div>
<div className='audioplayer'>
{AudioVisualizer(data.audio)}
{/* <ReactAudioPlayer src={data.audio} autoPlay controls/> */}
{/* <Waveform className="audio_file" audio={data.audio}/> */}
</div>
</div>
<div className='card_content_extra'>
<button onClick={() => navigate('/update/${data.id}')}>Edit</button>
<button >Listen</button>
</div>
{/* <div id="waveform"></div>
<button class="btn btn-primary" onclick="wavesurfer.playPause()">
<i class="glyphicon glyphicon-play"></i>Play/Pause
</button> */}
</article>
)
})}
</div>
}
</>
</div>
)
}
export default Home
UPDATE::
So as i described in my comment. When i am mapping through the songs object from my database, the waveform wont display. When i pass a direct link to the component it works. but when im passing my object "audio", and getting the value, , it will not show the waveform. When i try to console.log(data.audio) // it returns undefined.
see for yourself: As you can see from the console.log, it acts weird..
The reference to the DOM element is accessed by the .current property Not the reference object created by React.
You could use the useEffect hook, to load the data.
Then create the AudioVisualizer Component in the JSX react way and pass the link to the wavesurfer.
Also the wavesurfer dom object need to have some size.
Have a look at this mini example:
import React from 'react';
import ReactDOM from 'react-dom/client';
import { useRef, useEffect } from 'react';
import wavesurfer from 'wavesurfer.js'
const AudioVisualizer = (props) => {
const audioRef = useRef();
useEffect(()=>{
if (audioRef.current){
let audioTrack = wavesurfer.create({
container: audioRef.current,
});
audioTrack.load(props.link);
}
})
return <div style={{minWidth: "200px"}} className='audio' ref={audioRef}></div>
}
function App(props) {
return (
<div className='App'>
<AudioVisualizer link={"https://actions.google.com/sounds/v1/science_fiction/creature_distortion_white_noise.ogg"}></AudioVisualizer>
</div>
);
}
ReactDOM.createRoot(
document.querySelector('#root')
).render(<App />)

Error : "createSliderWithTooltip is not a function"

Im trying to implement the rc-slider for the web app however the rc-slider tooltip isnt recognized and shows an error of "createSliderWithTooltip is not a function" , which im not sure why .
For implementation of rc-slider i followed the rc-slider documentation which is the same way i have implemeneted in the code of home.js somehow im getting an error in console and nothing shows at all.
Thanks in advance.
Home.js
import React, { Fragment, useEffect , useState } from 'react'
import MetaData from './layouts/MetaData'
import { useDispatch , useSelector } from 'react-redux'
import { getProducts } from '../actions/products'
import Product from './products/Products'
import Loader from './layouts/Loader'
import { useAlert } from 'react-alert'
import Pagination from "react-js-pagination";
import {useParams} from 'react-router-dom'
import Slider from 'rc-slider'
import 'rc-slider/assets/index.css';
const { createSliderWithTooltip } = Slider;**//Error occurs here**
const Range = createSliderWithTooltip(Slider.Range)
const Home = () => {
const [currentPage,setCurrentPage]=useState(1);
const [price,setPrice]=useState([1,1000]);
let params=useParams();
const dispatch= useDispatch();
const alert=useAlert();
const {loading,products,error,productsCount,resPerPage,filteredProductsCount }= useSelector(state=>state.products)
const keyword=params.keyword;
useEffect(() => {
if (error) {
return alert.error("error");
}
dispatch(getProducts(keyword, currentPage));
}, [dispatch, alert, error, currentPage, keyword]);
function setCurrentPageNo(pageNumber) {
setCurrentPage(pageNumber)
}
return (
<Fragment>
{loading ? <Loader>Loading ...</Loader>:(
<Fragment>
<MetaData title={'Buy Electronics , Household Items and Many Others Online'} />
<h1 id="products_heading">Latest Products</h1>
<section id="products" className="container mt-5">
<div className="row">
<Fragment>
<div className="col-6 col-md-3 mt-5 mb-5">
<div className="px-5">
<Range
marks={{
1: `$1`,
1000: `$1000`
}}
min={1}
max={1000}
defaultValue={[1, 1000]}
tipFormatter={value => `$${value}`}
tipProps={{
placement: "top",
visible: true
}}
value={price}
onChange={price => setPrice(price)}
/>
</div>
</div>
</Fragment>
{products.map(product => (
<Product key={product._id} product={product} col={4} />
))}
</div>
</section>
<div className="d-flex justify-content-center mt-5">
<Pagination
activePage={currentPage}
itemsCountPerPage={resPerPage}
totalItemsCount={productsCount}
onChange={setCurrentPageNo}//sets current page no as it changes for state management
nextPageText={'Next'}
prevPageText={'Prev'}
itemClass="page-item"
linkClass="page-link"
/>
</div>
</Fragment>
)
}
</Fragment>
)}
export default Home
Instead of const { createSliderWithTooltip } = Slider;, try this:
const createSliderWithTooltip = Slider.createSliderWithTooltip;
I tried several ways and the only thing that actually worked was downgrading to 9.6.5 rc-slider and now everything is working perfectly
The document hasn't been updated yet since new version. As it seems you want to use Range component, now here the way to do it (thanks to Ashvin-Pal): https://github.com/react-component/slider/issues/825#issuecomment-1084416952
The createSliderWithTooltip has been removed in the new version.
Instead, you can implement your custom handle or tooltip easily like this:
handleRender={renderProps => {
return (
<div {...renderProps.props}>
<SliderTooltip>{round}%</SliderTooltip>
</div>
);
}}
let me know if you have any questions.

How to save the rating to localstorage so it doesn't dissapear after refreshing?

How can I let the star rating stay even after I leave the details page or after refreshing? Setting of the state isnt enough here. I suppose Localstorage type of saving should be used here right? Not really sure how to do that here. Hope you can help. Apreciate any advice guys. Thanks so much :))).
Details.js
import { useParams } from 'react-router-dom';
import './Details.css';
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import Image from './vitaminDfood-1132105308-770x553.jpg';
import {Link} from 'react-router-dom'
import ReactStars from 'react-rating-stars-component'
import { RecipeContext } from './RecipeContext';
import { useContext } from 'react';
function Details() {
const [details, setDetails] = useState([]);
const { recipeId } = useParams();
const[rating,setRating]=useState([])
const{recipes,setRecipes}=useContext(RecipeContext)
useEffect(() => {
axios
.get(`https://cookbook.ack.ee/api/v1/recipes/${recipeId}`)
.then((res) => setDetails(res.data));
});
const ratingChanged = (newRating) => {
var rate={
score:newRating
}
setRating(newRating)
axios.post(`https://cookbook.ack.ee/api/v1/recipes/${recipeId}/ratings`,rate)
.then((res) => {
console.log(res.data)
setRecipes(recipes)
})
};
return (
<>
<div className="details">
<div className="food-photo">
<img src={Image} alt="" />
<Link to="/"> <i className="fas fa-arrow-left arrow"></i></Link>
<h1>{details.name}</h1>
</div>
<div className="star-line">
{new Array(rating).fill(null).map(() => (
<i className="fas fa-star stari"/>
))}
<p className="duration"><i className="far fa-clock"></i>{details.duration}<span>min.</span></p>
</div>
<p className="info">{details.info}</p>
<h1 className="ingredience">Ingredience</h1>
<div className="ing">{details.ingredients}</div>
<h1 className="ingredience">Příprava</h1>
<p className="description">{details.description}</p>
</div>
<div className="stars">
<ReactStars
classNames="star"
size={48}
onChange={ratingChanged}
count={5}
value={1}
edit
/>
</div>
</>
);
}
export default Details;
You can use an effect with the useEffect hook to save your ratings to localStorage. On page load, you can load initial ratings from localStorage as well and use that as the default value in useState.
const initialRatings = JSON.parse(localStorage.getItem("ratings") || "[]");
function Details() {
const [ratings, setRatings] = useState(initialRatings);
// Save to localstorage on change
useEffect(() => {
localStorage.setItem("ratings", JSON.stringify(ratings));
}, [ratings])
}

ReactJS, functional components. So my state is given an initial value, then becomes undefined, and is then given a value during my useEffect

The part where my state becomes undefined prevents me from using the state later on in my return statement. I commented out where I want to use it so I can see the console logs, which is shown at the end of this post.
import React, {useState, useEffect} from 'react';
import axios from 'axios';
import {Card} from 'react-bootstrap';
import {Button} from "react-bootstrap";
const StrainDetails = (props) => {
// const [strains, setStrains] = useState([]);
const [details, setDetails] = useState(['hi']);
const [savedList, setSavedList] = useState([]);
let strain = Object.keys(props.strains).slice(0, 20).map((y) => {return y});
console.log(props)
console.log(strain)
useEffect(() => {
setDetails(props.strains[props.match.params.strain]);
}, [props.match.params.strain, props.strains])
console.log(details);
const addToSavedList = strain => {
setSavedList([...savedList, strain]);
console.log(savedList);
};
const saveStrain = () => {
addToSavedList(strain);
}
return (
<div className='container'>
<Card>
<div className="strain-card">
<div className='name'>
{props.match.params.strain}
</div>
{/* <div className='id'>
ID: {details.id.toString()}
</div> */}
{/* <div className='type'>
Type: {details.race.toString()}
</div>
<div className='flavors'>
Flavors: {details.flavors.toString()}
</div>
<div className='effects'>
Medical effects: {details.effects.medical.toString()} <br />
Positive effects: {details.effects.positive.toString()} <br />
Negative effects: {details.effects.negative.toString()} <br />
</div> */}
<Button className='save' onClick={saveStrain}>Save</Button>
</div>
</Card>
</div>
)
}
export default StrainDetails;
Here is a pic of my console, check the log for StrainDetails.js:17
I want to make it so the state never becomes undefined. That way I can use the state without any errors, but I'm not sure what is causing it to become so.
props.strains object data
props.strains is a very large data object, but right over it in the console where it says "hi", setDetails should already be triggered. (["hi"] is the useState for details).
Check props.match.params.strain and props.strains are valid before setting it to the details.
useEffect(() => {
if(props.strains.length > 0 && props.match.params.strain) {
setDetails(props.strains[props.match.params.strain]);
}
}, [props.match.params.strain, props.strains])
You have to use a Router and withRouter(), to access props.match
import {withRouter} from "react-router-dom";
export default withRouter(StrainDetails);

Resources