I'm new to react and trying to learn by building this simple app. I want to expose data from parent app in a child component but my result in the component appears to be empty.
what should I do to get the result to appear and re-render in the child component?
import React, { useState, useEffect } from "react";
import socketIOClient from "socket.io-client";
import Main from "./Main";
import Response from "./response"
const ENDPOINT = "http://127.0.0.1:4001";
function App() {
const [response, setResponse] = useState([]);
useEffect(() => {
const socket = socketIOClient(ENDPOINT);
socket.on("tick", (data) => {
setResponse(data);
});
}, []);
console.log(response);
return (
<>
<div style={{ textAlign: "center" }}>
<Main
responseCurrency = {response.map(memb => <div>{memb.currencyPairName}</div>)}
dataObject = {response}
/>
</div>
</>
);
}
export default App;
import './Main.css';
function Main(props) {
const [result, setResult] = React.useState(props.dataObject);
React.useEffect(() => {
console.log(result)
setResult(result);
}, [result]);
return <div className="container">
{result[0]}
<div id="palceholder1" className="placeholder">hello</div>
<div id="palceholder2" className="placeholder"></div>
<div id="palceholder3" className="placeholder"></div>
</div>;
}
export default Main;
I am a beginner in ReactJS, so I'm lost at the moment.
Hope someone could shed a light..
Thanks in advance
Gali
function Main(props) {
const [result, setResult] = React.useState(props.dataObject);
The problem is that you're copying props into state. The above line of code says to create a new state for Main, who's initial value is props.dataObject. When props.dataObject changes later, nothing will happen to this state.
Instead, you should use the prop directly:
function Main(props) {
return (
<div className="container">
{props.dataObject[0]}
<div id="palceholder1" className="placeholder">
hello
</div>
<div id="palceholder2" className="placeholder"></div>
<div id="palceholder3" className="placeholder"></div>
</div>
);
}
Related
this problem has already been written, but it hasn't been solved, so I'll upload it again, so please understand. I made a function that registers an image in child component, and makes the image visible in parent component, and erases the image by also setting the value of url to "" in parent component. I can see the image well, but when I erase the image, the url of the image is erased and then the original url value is entered again. I think there is a problem in the process of passing the url value from child component to parent component as a function. I received the following answer in the previous article, and I think this is the right reason, but I don't know how to modify the code. I'd appreciate it if you let me know, thanks.
On deleting image in parent component you need to pass that state to the child and make sure it is in sync with the similar state in child. Else the child state preview will always have a value and since the toParent callback isn't wrapped in useEffect hook it'll run everytime setting a value to isUrl state. You could move all the code in useEffect and toParent callback inside handleChange method.
Cild.jsx:
this is child component. Upload the image here and pass the url value to parent component through 'toParent'
import React, { useEffect, useState } from 'react'
function Child({toParent}) {
//file upload functions
const fileInput = React.useRef(null);
const [isfile,setIsfile] = useState("");
const handleButtonClick = e => {
fileInput.current.click();
};
const handleChange = e => {
setIsfile(e.target.files[0]);
console.log(e.target.files[0]);
};
const [preview, setPreview] = useState('');
useEffect(() => {
if (isfile) {
const objectUrl = URL.createObjectURL(isfile);
setPreview(objectUrl);
}
return () => URL.revokeObjectURL(isfile);
}, [isfile]);
//pass state to parent
toParent(preview)
return (
<h1>
<input
type="file"
style={{display:'none'}}
ref={fileInput}
onChange={handleChange}
multiple={true}/>
<button onClick={handleButtonClick}>
upload
</button>
</h1>
)
}
export default Child;
App.js:
and this is parent component. Get the url value here and show the image. Also, if I press delete, I want to empty the url of the image, but I can't. How can I empth the url??
import { useState } from "react";
import Child from "./Child";
function App() {
//receive state from child
const [isUrl,setIsUrl] = useState("")
const toParent = (url) => {
setIsUrl(url);
}
//delete image
const handelDelete = (e) => {
setIsUrl(" ")
}
return (
<div className="App">
<Child toParent={toParent} />
<div>
<img
style={{width:'300px', height:'300px'}}
src={isUrl}/>
</div>
<div>
<div onClick={handelDelete}>
delete
</div>
</div>
</div>
);
}
export default App;
I don't exactly why yours is not working, i have simplified the use of the state, try this:
Child.jsx:
import React, { useEffect, useState } from "react";
function Child({ setUrl }) {
const fileInputRef = React.useRef(null);
const [file, setFile] = useState("");
const handleButtonClick = (e) => {
fileInputRef.current.click();
};
const handleChange = (e) => {
setFile(e.target.files[0]);
console.log(e.target.files[0]);
};
useEffect(() => {
if (file) {
const objectUrl = URL.createObjectURL(file);
setUrl(objectUrl);
}
return () => URL.revokeObjectURL(file);
}, [file]);
return (
<h1>
<input
type="file"
style={{ display: "none" }}
ref={fileInputRef}
onChange={handleChange}
multiple={true}
/>
<button onClick={handleButtonClick}>upload</button>
</h1>
);
}
export default Child;
App.js:
import { useState } from "react";
import Child from "./Child";
function App() {
const [url, setUrl] = useState("")
const handleDelete = () => {
setUrl("");
}
return (
<div className="App">
<Child setUrl={setUrl} />
<div>
<img
style={{ width: '300px', height: '300px' }}
src={url} />
</div>
<div>
<div onClick={handleDelete}>
delete
</div>
</div>
</div>
);
}
export default App;
Some of the browsers cache the React code. So, try to clear the browser cache.
Check this link, how to clear browser cache in reactjs
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;
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 />)
I´m getting an error when trying to render an image in component.
I paste the code here.
Is it possible that I need a babel or webpack plugin?
In this component, the image rendering works fine:
import React from "react";
function ProductItem({ product }) {
return product ? (
<div>
<div>
<img src={product.images[0]} alt={product.title} />
</div>
<div>
{product.title}
<br />
${product.price}
</div>
<p>{product.description}</p>
</div>
) : <p>Loading Product... </p>;
};
export default ProductItem;
In this other component is where I have the problem.
ProductDetail.js
import React from "react";
import useGetProducts from "../hooks/useGetProducts";
const API = 'https://api.escuelajs.co/api/v1/products';
function ProductDetail() {
const data = useGetProducts(`${API}/6`);
return (
<>
{data.products
?
<>
<h3>{data.products.title}</h3>
<p>{data.products.description}</p>
<div>
<img src={data.products.images[0]} alt="title" />
</div>
</>
: <h4>Loading...</h4>
}
</>
);
}
export default ProductDetail;
Custom Hook with useEffect, the useGetProducts function is responsible for bringing the data from the API with the Axios library
import { useEffect, useState } from "react";
import axios from "axios";
const useGetProducts = (API) => {
const [products, setProducts] = useState([])
const [error, setError] = useState("");
const [loaded, setLoaded] = useState(false);
useEffect(() => {
(async () => {
try {
const response = await axios(API);
setProducts(response.data);
} catch (error) {
setError(error.message);
} finally {
setLoaded(true);
}
})();
}, []);
return { products, error, loaded };
};
export default useGetProducts
Your default state for products is [], so the conditional render data.products in ProductDetail.js always return true so you can change default state for products is null
const [products, setProducts] = useState(null);
The first answer is correct, so I will not duplicate it, but I see room for improvement in your code/example.
Your useGetProducts hook is very easy to break and hard to reuse. If you will pass the wrong URL or the structure of the API will change it will break your code. Also, the hook is not very generic, cause you will need to create similar fn for each entity. My suggestion. Use react-query and separate functions for calling API. So it will look like this.
import { useQuery } from 'react-query'
import axios from 'axios'
export default function ProductPage() {
const productResponse = useQuery('exchanges', () => getProduct('6'))
const { isLoading, isError, data: product } = productResponse
return (
<div>
{isLoading && <div>Loading...</div>}
{isError && <div>Something went wrong :(</div>}
{product && (
<div>
<h1>Product title: {product.title}</h1>
<p>
{product.images.map(imageSrc => (
<img key={imageSrc} src={imageSrc} alt="" />
))}
</p>
</div>
)}
</div>
)
}
interface Product {
id: string
title: string
images: string[]
}
function getProduct(id: string): Promise<Product> {
return axios
.get(`https://api.escuelajs.co/api/v1/products/${id}`)
.then(r => r.data)
}
PS. react-query requires additional configuration ( context provider, config, etc ). Please look into docs on how to use it.
The app uses useContext for state management and axios for a get request to an API to receive data. Originally I was not using useContext but later realized state will be needed in multiple components later down the road and props would be messy. The app was working perfectly prior to using useContext now I am receiving a blank screen and no error messages.
ThemeContext.js
import {useState, useEffect, createContext} from 'react'
import axios from 'axios'
const ThemeContext = createContext()
const ThemeContextProvider = props => {
const [students, setStudents] = useState([])
const [loading, setLoading] = useState(false)
useEffect(()=>{
getStudents()
},[])
const getStudents = async () => {
try {
const res = await axios.get('https://api.hatchways.io/assessment/students')
setStudents(res.data.students)
setLoading(true)
}
catch (err) {
console.log(err.message)
}
}
return (
<ThemeContextProvider.Provider value={{students, loading}}>
{props.children}
</ThemeContextProvider.Provider>
)
}
export {ThemeContextProvider, ThemeContext}
Students.js
import {useContext} from 'react'
import {ThemeContext} from './themeContext'
const Students = props => {
const {students, loading} = useContext(ThemeContext)
return (
<div>
{loading &&
students.map((student) =>(
<div className="student-profile-container">
<div className="student-profile-image">
<img key={student.id} src={student.pic} alt="student profile avatar"/>
</div>
<div className="student-profile-info">
<h1 className="student student-name">{student.firstName} {student.lastName}</h1>
<p className="student student-info">Email: {student.email}</p>
<p lassName="student student-info">Company: {student.company}</p>
<p className="student student-info">Skill: {student.skill}</p>
<p className="student student-info">Average: {student.average}%</p>
</div>
</div>
))
}
</div>
);
}
export default Students;
It appears you are mixing up ThemeContext and ThemeContextProvider. Changing the return value of ThemeContextProvider should fix your issue.
<ThemeContext.Provider value={{students, loading}}>
{props.children}
</ThemeContext.Provider>