Passing down Props to "Single Page Views" through components - reactjs

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;

Related

How to store and access data from array [product] from context?

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.

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 />)

Data in React Component Not refreshing when Path/Location changes

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

Pass value from parent to child component with React

I trying to display the rating of a query in my React App. But I'm not sure if I understand how to handle the state.
This is my query component:
import React, { Component, useRef, useState, useEffect } from 'react';
import { render } from 'react-dom';
import InputSearchLandlord from './search'
import './style.css'
import SimpleRating from '../components/star_display'
import ReactStars from 'react-rating-stars-component'
import './style.css'
const HandleSearch = () => {
const [ratingValue, setRating] = useState(0)
const [name, searcName] = useState("")
const nameForm = useRef(null)
const average = arr => arr.reduce( ( p, c ) => p + c, 0 ) / arr.length;
const ratings = []
const displayComment = async() => {
try {
const form = nameForm.current
const name = form['name'].value
searchName(name)
const response = await fetch(`localhost`)
const jsonData = await response.json()
getComments(jsonData)
comments.forEach(e => {
console.log(e.rating)
ratings.push(e.rating)
})
const rating = average(ratings) //Avg of all rating associated with the search
console.log(rating) //Should be pass to Rating component
setRating(rating)
} catch (error) {
console.log(error.message)
}
}
return(
<div className="container">
<div className="form-group">
<h1 className="text-center mt-5">SEARCH</h1>
<form ref={nameForm} className="mt-5">
<InputSearch name={'name'}/>
<div className="d-flex justify-content-center">
<button type="submit" className="d-flex btn btn-primary" onClick={displayComment}>Search</button>
</div>
</form>
<div>
<div className='container'>
<h1>{name}</h1>
<SimpleRating data={ratingValue}
/>
</div>
<div className='container'>
{comments.map(comment => (
<div className="commentSection">
<a>
{comment.problem}
</a><br/>
<a>
Posted on : {comment.date}
</a>
</div>
))}
</div>
</div>
</div>
</div>
)
}
export default HandleSearch;
And this is my Rating component:
import React, { useState } from 'react';
import { render } from 'react-dom';
import ReactStars from 'react-rating-stars-component'
import './style.css'
import HandleSearch from '../pages/handleSearch'
export default function SimpleRating(rating) {
const [ratingValue, setRating] = useState(0)
const options = {
value: ratingValue, //Should use the value from the Search component
a11y: true,
isHalf: true,
edit: false,
};
console.log(options.value)
if (options.value == 0) return null //if rating value = 0 doesn't display the component
return (
<div className="starComponent">
<ReactStars {...options}/>
</div>
);
}
So I trying to pass the value computed in the Search component to the Rating component. Before any query is made with the Search component, the value should be 0 and hidden.
What am I missing ?
Its to do with your props. In your parent component you create a prop called data so in your rating component you need to extract that value from props
// HandleSearch Component
<SimpleRating data={ratingValue}
export default function SimpleRating(props) {
const { data } = props
// You can also just say props.data
... rest of your component
}
Currently you are actually defining the props in your SimpleRating component but you are calling them rating (it doesn't actually matter what you call it but commonly its called props) and that is an object that contains all of the props that you pass into that component.

React shows unfinished elements before the page renders completely

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.

Resources