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 />)
Related
I have a context which contains a fetch() method used to retieve list of products from a server.I made this context so that I could reuse the fetched array values every webpage I might need.But I am unable to do so as it gives me an error in console.
this is the code for context
import React, { createContext, useState, useEffect } from 'react'
export const ProductContext = createContext()
const ProductContextProvider = (props) => {
const [product, setProduct] = useState([]);
const fetchData = () => {
fetch(`http://localhost:8080/product`)
.then((response) => response.json())
.then((actualData) => {
setProduct(actualData)
console.log(product);
})
};
useEffect(() => {
fetchData();
}, [])
return (
<ProductContext.Provider
value={{ product }}>
{props.children}
</ProductContext.Provider>
)
}
export default ProductContextProvider
and this is the error I am getting in console
enter image description here
I have done this too in index.js
enter image description here
and this is one page I want to call the product[]
import React from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import { useContext } from 'react'
import ProductContext from '../context/ProductContext';
function Product() {
const { product } = useContext(ProductContext)
console.log(product);
return (
<div className="products-row ">
{
product.map((data, num) => {
return (
<div className="product" key={num}>
<div className="card">
<a target="_blank" href="/design" >
<img src={data.thumbnail} alt={data.name} style={{ width: "100%" }} ></img>
</a>
<h1>{data.name}</h1>
<p className="price">${data.price}</p>
</div>
</div>
)
})
}
</div>
);
}
export default Product;
I believe it's an import issue. You probably meant to use the following:
import { ProductContext } from '../context/ProductContext';
Right now, your ProductContext is actually ProuductContextProvider, which is the default export as per your code.
I have a react app that has a "Bread Crumb Header" component, the data for this component comes from an API end point.
I use the bread crumb header component inside mulitiple components within the app, and based on the current path/window.location the bread crumb componet will get the data from the API and render the correct HTML via JSX.
The problem I have is when I navigate to diffent paths/window.location's within the application the bread crum component data doesn't update.
This is what the bread crumb component looks like:
import React, { useState, useEffect } from 'react';
import API from "../../API";
import { useLocation } from 'react-router-dom';
import { BreadCrumbTitleSection, SubtitleSection, Subtitle } from './breadCrumbHeaderStyle';
import { Breadcrumb } from 'react-bootstrap';
function BreadCrumbHeader() {
const location = useLocation();
const [breadCrumbData, setBreadCrumbData] = useState([]);
const getBreadCrumbData = async () => {
const breadCrumbHeaderResponse = await API.fetchBreadCrumbHeader(location.pathname);
setBreadCrumbData(breadCrumbHeaderResponse);
};
useEffect(() => {
getBreadCrumbData();
}, []);
return (
<div>
<BreadCrumbTitleSection backgroundUrl={breadCrumbData.BreadCrumbBgImage}>
<div className="container">
<div className="row no-gutters">
<div className="col-xs-12 col-xl-preffix-1 col-xl-11">
<h1 className="h3 text-white">{breadCrumbData.BreadCrumbTitle}</h1>
<Breadcrumb>
{breadCrumbData.BreadCrumbLinks.map(breadCrumbLink => (
<Breadcrumb.Item href={breadCrumbLink.LinkUrl} key={breadCrumbLink.Id} active={breadCrumbLink.IsActive}>
{breadCrumbLink.LinkText}
</Breadcrumb.Item>
))}
</Breadcrumb>
</div>
</div>
</div>
</BreadCrumbTitleSection>
<SubtitleSection>
<Subtitle> {breadCrumbData.SubTitle}</Subtitle>
</SubtitleSection>
</div>
);
}
export default BreadCrumbHeader;
and this is an example of how I am using it inside other components:
import React, { useContext } from 'react';
import { useParams } from "react-router-dom";
import { MenuContext } from '../context/menuContext';
import RenderCmsComponents from '../../components/RenderCmsComponents/';
import BreadCrumbHeader from '../../components/BreadCrumbHeader/';
import { CategorySection, CategoryContainer, CategoryItemCard, CategoryItemCardBody, CategoryItemCardImg, CategoryItemTitle, CategoryRow, AddToCartButton, ProductDescription} from './categoryStyle';
function Category() {
const [categoryItems] = useContext(MenuContext);
const { id } = useParams();
const category = categoryItems.find(element => element.CategoryName.toLowerCase() === id.toLowerCase());
var dynamicProps = [];
{
category && category.Products.map(productItem => (
dynamicProps.push(productItem.ProductOptions.reduce((acc, { OptionName, OptionsAsSnipCartString }, i) => ({
...acc,
[`data-item-custom${i + 1}-name`]: OptionName,
[`data-item-custom${i + 1}-options`]: OptionsAsSnipCartString
}), {}))));
}
return (
<div>
<BreadCrumbHeader /> << HERE IT IS
<CategorySection backgroundurl="/images/home-slide-4-1920x800.jpg" fluid>
<CategoryContainer>
<CategoryRow>
{category && category.Products.map((productItem, i) => (
<CategoryItemCard key={productItem.ProductId}>
<CategoryItemTitle>{productItem.ProductName}</CategoryItemTitle>
<CategoryItemCardBody>
<ProductDescription>{productItem.Description}</ProductDescription>
<div>
<CategoryItemCardImg src={productItem.ProductImageUrl} alt={productItem.ProductName} />
</div>
</CategoryItemCardBody>
<AddToCartButton
data-item-id={productItem.ProductId}
data-item-price={productItem.Price}
data-item-url={productItem.ProductUrl}
data-item-description={productItem.Description}
data-item-image={productItem.ProductImageUrl}
data-item-name={productItem.ProductName}
{...dynamicProps[i]}>
ADD TO CART {productItem.Price}
</AddToCartButton>
</CategoryItemCard>
))}
</CategoryRow>
</CategoryContainer>
</CategorySection>
<RenderCmsComponents />
</div>
);
}
export default Category;
I found this post on stack overflow:
Why useEffect doesn't run on window.location.pathname changes?
I think this may be the solution to what I need, but I don't fully understand the accepted answer.
Can someone breakdown to be how I can fix my issue and maybe give me an explaination and possible some reading I can do to really understand how hooks work and how to use them in my situation.
It seems that you should re-call getBreadCrumbData every time when location.pathname was changed. In the code below I've added location.pathname to useEffect dependency list
const location = useLocation();
const [breadCrumbData, setBreadCrumbData] = useState([]);
const getBreadCrumbData = async () => {
const breadCrumbHeaderResponse = await API.fetchBreadCrumbHeader(location.pathname);
setBreadCrumbData(breadCrumbHeaderResponse);
};
useEffect(() => {
getBreadCrumbData();
}, [location.pathname]); // <==== here
Hey still new to React but I'm grinding my way through it slowly by building my own personal app/platform. I have a quick question of passing down props to single page views. This is my overview page that will pull in all the teams from my database as such:
import React, { useState, useEffect } from 'react';
import firebase from '../../firebase/firebase.utils'
import Button from '../../Components/GeneralComponents/Button.component'
import * as GoIcons from 'react-icons/go';
import TeamList from '../../Components/Teams/TeamList.Component'
function TeamsPage() {
const [teams, setTeams] = useState([]);
const [loading, setLoading] = useState(false);
const ref = firebase.firestore().collection("teams");
function getTeams() {
setLoading(true);
ref.onSnapshot((querySnapshot) => {
const items = [];
querySnapshot.forEach((doc) => {
items.push(doc.data());
});
setTeams(items);
setLoading(false);
console.log(items);
});
}
useEffect(() => {
getTeams();
},[])
if(loading) {
return <h1>Loading...</h1>
}
return (
<div className="content-container">
<h2>Team Page</h2>
<div className="add-section">
<div className="actions">
<Button
className="bd-btn outlined add-team"
><GoIcons.GoGear/>
Add Team
</Button>
</div>
</div>
<TeamList teams={teams} />
</div>
)
}
export default TeamsPage;
This gets passed into my TeamList Component:
import React from 'react';
import { Link } from 'react-router-dom'
import { TeamCard } from './TeamCard.Component';
const TeamList = props => {
return(
<div className='teams-overview'>
{props.teams.map(team => (
<Link to={`/teams/${team.id}`}>
<TeamCard key={team.id} team={team}/>
</Link>
))}
</div>
)
}
export default TeamList;
Which maps through and then list the Team as a card component with a link that is supposed to route to their id and pass through their data.
Now in my single page view of a team I'm struggling to gain access to that prop data:
import React from 'react'
function TeamSinglePage(team) {
return (
<div className="content-container">
<h1>Single Page View</h1>
<p>Welcome, {team.teamName}</p>
</div>
)
}
export default TeamSinglePage;
"EDITED"
I'm trying to make a very minimalist bar chart.
It doesn't, render. It seems that the child component isn't rendering after the parent component's state has changed.
for example. if I make a change in my code in the MiniChart component, and save the changed through my IDE. the charts will render correctly. but as soon as navigate to another page in my app through my browser and come back to where the charts are, then they won't render.
Any help will be much appreciated.
Child component:
import React, {useState, useEffect} from 'react';
import axios from 'axios';
import {BarChart, Bar} from 'recharts';
const MiniChart = (props) => {
const [apiUrl] = useState("https://api.coingecko.com/api/v3/coins/"+props.id+"/market_chart?vs_currency=usd&days=30&interval=daily");
const [data, setData] = useState([]);
const [msg, setMsg] = useState([]);
const [r, setR] = useState([]);
// fetch data from api
useEffect(() => {
const fetchData = async () => {
if(parseInt(props.rank) < 5){
const result = await axios(apiUrl,);
setData(result.data.prices);
} else {setMsg("TEST : not loaded");}
}
setR(data.map(elem => ({ 'val': elem[1]})));
fetchData();
return()=>{
}
}, [apiUrl, props.rank]);
return (
<div>
<BarChart width={150} height={40} data={r}>
<Bar dataKey='val' fill="green" />
</BarChart>
</div>
)
}
export default MiniChart
Parent component:
import React, { useState} from 'react'
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faStar } from "#fortawesome/free-solid-svg-icons";
import { Link, useLocation } from 'react-router-dom';
import Cookies from 'universal-cookie';
import MiniChart from './MiniChart';
const Crypto = (props) => {
const location = useLocation();
const [starColor, setStarColor] = useState(props.defaultStarCol);
const cookies = new Cookies();
const getFavs = cookies.getAll();
// toggle color, re-render, remove or add to cookies
const handleFavToggle = (e) => {
if(starColor === '#ebc934'){
setStarColor('lightgrey');
cookies.remove(props.id, props.id, { path: '/' });
if(location.pathname === '/favorites'){
function refreshPage() {
window.location.reload(false);
}
refreshPage();
}
} else {
setStarColor('#ebc934');
cookies.set(props.id, props.id, { path: '/' });
//console.log(cookies.getAll());
}
}
return (
<div>
<li>
<div className="lidiv">
{props.id in getFavs? //ADD IF LOGGED IN !
<p className="pml"><FontAwesomeIcon style={{color:'#ebc934'}} onClick={handleFavToggle} className="star" icon={faStar}/></p>
: <p className="pml"><FontAwesomeIcon style={{color:'lightgrey'}} onClick={handleFavToggle} className="star" icon={faStar}/></p>}
<p className="pml">{props.rank}</p>
<img className="iconimg" src={props.img} alt=""/>
<p className="pxl coinName"><Link to="/crypto" style={{display: 'block'}}>{props.coin}</Link></p>
<p className="pml">{props.tag}</p>
<p className="pml4">{props.price}</p>
<p className="pml" style={{color: (props.oneday).charAt(0)==='-' ? 'red': 'green'}}>{props.oneday}%</p>
<p className="pxl daycash" style={{color: (props.oneday).charAt(0)==='-' ? 'red': 'green'}}>{props.onedaycurr} </p>
<p className="pxl-4">{props.mcap}M</p>
<MiniChart className="pxl" id={props.id} rank={props.rank}></MiniChart>
</div>
</li>
</div>
)
}
export default Crypto;
I am making a posts project with React. Whenever My home page just consists a bunch of posts in cards view. If I click on a certain card, it should navigate me to that post with some details. However, before the page renders completely, I still see some lines or 'unfinished' elements so to speak.
This is what I see before the post and it's description render completely
How can I fix this?
Here is my JS file with posts:
import React, { Component } from 'react'
import { Card } from "react-bootstrap";
import { Link } from "react-router-dom";
import './Posts.css'
export class Posts extends Component {
render() {
return (
<div>
{this.props.posts.map(post => (
<Link to={`/post/${post._id}`} key={post._id}>
<Card key={post._id} className="shadow-sm">
<Card.Img variant="top" src={post.image} />
<Card.Body>
<Card.Title id="cardtitle">{post.title}</Card.Title>
</Card.Body>
</Card>
</Link>
))}
</div>
)
}
}
And here is the Post detail:
import React, { useState, useEffect } from 'react'
import axios from "axios";
import { Media } from "react-bootstrap";
import './Post.css'
function Post({ match }) {
const [post, setPost] = useState({});
useEffect(() => {
const fetchPost = async () => {
const res = await axios.get(`${POST_URL}${match.params.id}`);
setPost(res.data);
}
fetchPost();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const POST_URL = `/api/v1/posts/`;
return (
<Media>
<img className="align-self-center mr-3 postImage" src={post.image} alt={post.title} />
<Media.Body>
<h5 className="postTitle text-center">{post.title}</h5>
<p>{post.description}</p>
</Media.Body>
</Media>
)
}
export default Post
You can create a function inside your component that conditionally render if the data has returned from the API. Something like this:
import React, { useState, useEffect } from 'react'
import axios from "axios";
import { Media } from "react-bootstrap";
import './Post.css'
function Post({ match }) {
const [post, setPost] = useState();
useEffect(() => {
const fetchPost = async () => {
const res = await axios.get(`${POST_URL}${match.params.id}`);
setPost(res.data);
}
fetchPost();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const POST_URL = `/api/v1/posts/`;
const renderPost = () => {
if (post) {
return(
<Media>
<img className="align-self-center mr-3 postImage" src={post.image} alt={post.title} />
<Media.Body>
<h5 className="postTitle text-center">{post.title}</h5>
<p>{post.description}</p>
</Media.Body>
</Media>
);
}
return <h1>Loading</h1>;
}
return (
renderPost();
)
}
export default Post
I recommend you to render a spinner or a placeholder to improve the user experience while the data is loading.