I'm trying to render actual data in child component, but data does not render. What is wrong?
Parent component
const UserPanelContainer = ({ currentUser }) => {
const [initUsersData, setinitUsersData] = useState(currentUser);
useEffect(() => {
console.log('useEffect')
setinitUsersData(()=>getnewData())
}, [setinitUsersData, currentUser])
const getnewData = () =>{
console.log('getnewData')
setinitUsersData(currentUser)
}
return (
<UserPanel currentUser={initUsersData} hanleOnClickOut={hanleOnClickOut} >{console.log('usepanContainerRender')}</UserPanel>
);
};
export default UserPanelContainer;
child
const UserPanel = ({ currentUser, hanleOnClickOut }) => {
console.log(currentUser);
return (
<div className="dropdown">
{console.log('userPanelRender')}
<button
className="btn btn-secondary dropdown-toggle"
type="button"
id="dropdownMenuButton"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
<img
className="avatar"
src={currentUser.photoURL}
alt="avatar"
/>
{currentUser.displayName}
</button>
<div className="dropdown-menu" aria-labelledby="dropdownMenuButton">
<div className="dropdown-item">
Вошел как {currentUser.displayName}
</div>
<div className="dropdown-item" onClick={hanleOnClickOut}>
Выйти
</div>
</div>
</div>
);
};
export default UserPanel;
In console in child I can see correct actual data in props, but they are not rendered.
Actual data contains "currentUser" prop. But on Browser page i cant see data....
(if i delete currentUser from useEffect depencity i can see data from previus API call)
I see you are passing the setinitUsersData in the useEffect dependency array whereas you need to pass the actual state variable
try this,
useEffect(() => {
...
}, [initUsersData, currentUser])
instead of current,
useEffect(() => {
...
}, [setinitUsersData, currentUser])
I think by actual data you mean some api response.
Try this :-
useEffect(() => {
console.log('useEffect')
getnewData(currentUser)
}, [currentUser])
const getnewData = (currentUser) =>{
console.log('getnewData')
axios.get("/pathToData").then((res) => {
console.log(res);
setinitUsersData(res);
})
}
Replace parent component with the following code. You don't need to use useEffect as per the code you've posted. Since parent is already receiving currentUser and you have already updated state with that
const UserPanelContainer = ({ currentUser }) => {
const [initUsersData, setinitUsersData] = useState(currentUser);
return (<UserPanel
currentUser={initUsersData}
hanleOnClickOut={hanleOnClickOut}>{console.log('usepanContainerRender')}</UserPanel>
);
};
export default UserPanelContainer;
Related
So, the request is returning the JSON file. But when in console it is saying 'Undefined' and I do not know why.
So the button when clicked will send the results from my request from the google Place API; which contains the place_id needed to make the call to the Place Details API to the Info component.
const OnButtonClick = (restaurant) => {
setRestaurant(restaurant)
setOpenPopup(true)
}
<button className="cardButton" onClick={() => OnButtonClick(restaurantData)}>
View Information
</button>
<InfoPopup open={openPopup} restaurant={restaurant} onClose={() => setOpenPopup(false)} />
So, this works the way I think it does (Sorry, I am new to React)
Here's the InfoPopup component
function InfoPopup({ open, onClose, restaurant }) {
const [restaurant1, setRestaurant1] = useState([])
let id = restaurant.place_id
let URL = `/maps/api/place/details/json?place_id=${id}&key=${process.env.REACT_APP_API_KEY}`
const fetchRestaurants1 = async () => {
const res1 = await axios.get(URL)
setRestaurant1(res1.data.results);
}
useEffect(() => {
fetchRestaurants1()
console.log(restaurant1) //This is getting 'Undefined'
}, [id]);
const navigate = useNavigate()
if (!open) {return null}
return ReactDOM.createPortal(
<>
<div>
{restaurant1?.map(restaurant => (
<div key={restaurant.place_id}> {restaurant.formatted_phone_number} </div>
))}
</div>
<div className="popup">
<div className="popup-inner">
<button className="close-btn" onClick={onClose}> Close </button>
<h1 className="title"> {restaurant.name} </h1>
<ul>
{/* <li className="service">
Status: {}
</li> */}
<li className="location">
Address: {restaurant.vicinity}
Phone Number:
</li>
<li className="cost">
Cost: {restaurant.price_level}
</li>
{/* <li className="food">
Food Type:
</li> */}
</ul>
<div className="links">
<Link className="writeButton" to="/write" state={{data: restaurant}}>
Write a review
</Link>
{/* <button className="writeButton" onClick={() => navigate("/write", {data:restaurant})}>
Write a review
</button> */}
<Link className="readButton" to="/read" state={{data: restaurant}}>
Read the reviews
</Link>
{/* <button className="readButton" onClick={() => navigate("/read")}>
Read the reviews
</button> */}
</div>
</div>
</div>
</>,
document.getElementById('portal')
)
}
I think the problem is on the first render, there's no ID being passed. But I do not know how to work around it. Any help would be appreciated.
Looking at this block of code:
const fetchRestaurants1 = async () => {
const res1 = await axios.get(URL)
setRestaurant1(res1.data.results);
}
useEffect(() => {
fetchRestaurants1()
console.log(restaurant1) //This is getting 'Undefined'
}, [id]);
You're awaiting the result of the GET call, which is good, because it allows you to set state in the next line after waiting for the response.
The problem: when you call fetchRestaurants1() in the useEffect(), you're not waiting for that function to execute, therefore, we jump straight to the next line and console.log() restaurant1, which is of course still blank.
The same issue arises with set state calls.
If you do:
const [value, setValue] = useState(null);
then sometime later:
setValue(5);
console.log(value);
The value posted to console will be null, because JS doesn't want to wait for the set state call to finish before moving onto the next line, console.log(value);
To fix this: make the useEffect callback async, and await the functions in which you're making your axios.get calls. Example:
const fetchSomeData = async () => {
const response = await axios.get(URL);
setData(response.data);
}
useEffect(async () => {
await fetchSomeData();
/* do some stuff */
}, []);
Of course, you still can't console.log after the set state call above.
If you want a generalizable way to log state changes without worrying about async behavior, you can add a simple useEffect:
useEffect(() => {
console.log(value);
}, [value]);
Where value is any state variable. Since value is in the dependency array, anytime it changes, the useEffect will fire and log the change.
I have an issue with useEffect, expect it to re render grid of elements, when on of the elements in this grid was deleted and a variable that contains data of that elements was changed, use effect refers to this variable so I expect it to re render grid where it placed.
const TasksView = ({current, tasksViewTriggered, cancelModals, addTaskToNote}) => {
const [tasks, setTasks] = useState([])
const onAddTask = () => {
addTaskToNote(current.id, '', false)
}
const onCancel = () => {
cancelModals()
}
useEffect(() => {
console.log('current was changed')
if (current) {
setTasks(current.tasks)
}
}, [current, tasksViewTriggered])
return (
<div className={`tasks-modal ${tasksViewTriggered ? '' : 'invisible'}`}>
<div className='taskModalContent'>
<div className="tasks-grid">
{tasks.length === 0 ? (<p className='center'>No tasks to show</p>) : (
tasks.map(task => <Task task={task} key={task.id}/>)
)}
</div>
</div>
<div className='modalFooter'>
<a href='#!' onClick={onCancel} className='btn btn-red'>
Close
</a>
<a href='#!' onClick={onAddTask} className='btn btn-green'>
Add Task
</a>
</div>
</div>
);
};
TasksView.propTypes = {
current: PropTypes.object,
tasksViewTriggered: PropTypes.bool.isRequired,
cancelModals: PropTypes.func.isRequired,
addTaskToNote: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
current: state.note.current,
tasksViewTriggered: state.note.tasksViewTriggered
})
export default connect(
mapStateToProps,
{cancelModals, addTaskToNote}
)
(TasksView);
Current value changes on task deletion and I expect this page to be re rendered after, but it does not happen.
useEffect(() => {
console.log('current was changed')
if (current) {
setTasks(current.tasks)
}
}, [current.tasks, tasksViewTriggered])
be sure current will not be null
import React from 'react'
import { useState, useEffect } from 'react'
import axios from 'axios'
const Home = () => {
const getSongs = () => {
axios.get('http://localhost:8000/api/songs/')
.then(res => setSongs(res.data))
}
let [songs, setSongs] = useState([])
let [paused, setPause] = useState(true)
useEffect(() => {
getSongs()
}, [])
const toggleSong = (id) => {
const x = document.getElementById(id)
if (x.paused){
x.play()
setPause(false)
} else {
x.pause()
setPause(true)
}
}
// Got rid of the functions that are not needed
return (
<>
{
songs.map(song =>
(
<div className='music-controller' key={song.id}>
<div id={'songDiv'} style={{cursor: 'pointer'}} onClick={(e) => changeSongTime(e, song.id)}>
<div id={`songTime-${song.id}`}></div>
</div>
<div className="music-controller-body">
<div className="music-controller-header">
<h2>{song.title}</h2>
<p><small>{song.genre}</small></p>
</div>
<div className="controls">
// here <----------------------
<i unique={song.id} className={`fas fa-${paused ? 'play' : 'pause'}`} onClick={() => toggleSong(song.id)}></i>
<audio id={song.id} onTimeUpdate={() => songTime(song.id)}>
<source src={`http://localhost:8000/api/songs/audio/${song.id}`} />
</audio>
</div>
</div>
</div>
))}
</>
)
}
export default Home
Whenever I click on a specific i element all of the i elements that were not clicked on get changed too.. to put it simply when I click on the 1st i element only its className should change, but all of the i elements classNames are affected, what is causing this?
I think you should use event.target
const handlePlay = (song) => {
song.play();
};
const handlePause = (song) => {
song.pause();
};
...
<div className="controls">
<i
onMouseOver={(e) => handlePlay(e.target)}
onMouseLeave={(e) => handlePause(e.target)}
className={`fas fa-${paused ? 'play' : 'pause'}`}
onClick={() => toggleSong(song.id)}>
</i>
<audio id={song.id} onTimeUpdate={() => songTime(song.id)}>
<source src={`http://localhost:8000/api/songs/audio/${song.id}`} />
</audio>
</div>
I don't think Toggle would work in this case, an action should happen so it knows when it should stop.
Can you put console in toggleSong function at top and check if you are getting correct id. If you are not getting single Id then work is needed with onClick. So, after that also try passing id like this
onClick={(song?.id) => toggleSong(song?.id)}
then see console again and look for correct id if it is displayed or not. I think your className is not updating due to this issue.
One thing more you can try at end is replacing with this
const x = id; //without document.getElementById
const toggleSong = (e, id) => {
const x = document.getElementById(id)
const button = e.currentTarget
if (x.paused){
x.play()
button.className = 'fas fa-pause'
} else {
x.pause()
button.className = 'fas fa-play'
}
}
<i unique={song.id} className='fas fa-play' onClick={(e) => toggleSong(e, song.id)}></i>
I fixed this by just getting the current target with event.currentTarget and change its className accordingly!
The this keyword inside the vidsAsHtml mapping function keeps returning undefined.
I read this, and a couple other SO questions about this but their solutions did not solve the problem. I'm already using es6 syntax arrow function for the map, but I've also tried putting in this as a second argument, which didn't solve the issue. Curious if anyone knows why 'this' keyword keeps coming up as undefined here.
import React, { useState, useEffect } from 'react'
import axios from 'axios'
const VideoGrid = (props) => {
const [videos, setResource] = useState([])
const fetchVideos = async (amount, category) => {
const response = await axios.get('https://pixabay.com/api/videos/', {
params: {
key: '123456679',
per_page: amount,
category: category
}
})
console.log(response)
const vidsAsHtml = response.data.hits.map(vid => {
return (
<div className={`${props.page}--grid-content-wrapper`} key={vid.picture_id}>
<div className={`${props.page}--grid-video`}>
<video
poster="https://i.imgur.com/Us5ckqm.jpg"
onMouseOver={this.play()}
onMouseOut={this.pause()}
src={`${vid.videos.tiny.url}#t=1`} >
</video>
</div>
<div className={`${props.page}--grid-avatar-placeholder`}></div>
<div className={`${props.page}--grid-title`}>{vid.tags}</div>
<div className={`${props.page}--grid-author`}>{vid.user}</div>
<div className={`${props.page}--grid-views`}>{vid.views}
<span className={`${props.page}--grid-date`}> • 6 days ago</span>
</div>
</div>
)
})
setResource(vidsAsHtml)
}
useEffect(() => {
fetchVideos(50, 'people')
}, [])
return (
<main className={`${props.page}--grid-background`}>
<nav className={`${props.page}--grid-nav`}>
<button
id='followButton'
className={`${props.page}--grid-nav-${props.titleOne}`}
>{props.titleOne}
</button>
<button
id='recommendedButton'
className={`${props.page}--grid-nav-${props.titleTwo}`}
>{props.titleTwo}
</button>
<button
id='subscriptionsButton'
className={`${props.page}--grid-nav-${props.titleThree}`}
>{props.titleThree}
</button>
<button className={`${props.page}--grid-nav-${props.titleFour}`}>{props.titleFour}</button>
<button className={`${props.page}--grid-nav-${props.titleFive}`}>{props.titleFive}</button>
<button className={`${props.page}--grid-nav-follow`}>FOLLOW</button>
</nav>
<hr className={`${props.page}--grid-hr-nav-grey`} />
<hr className={`${props.page}--grid-hr-nav-black`} />
<div className={`${props.page}--grid`} style={{marginTop: 'unset'}}>
{videos}
</div>
</main>
)
}
export default VideoGrid
Event handler props are expected to be passed a function. Currently you are trying to pass the return values of this.play() and this.pause() as event handlers, which wouldn't work anyway.
Also React doesn't make the element available to the event handler via this, but you can access it via event.target:
<video
poster="https://i.imgur.com/Us5ckqm.jpg"
onMouseOver={event => event.target.play()}
onMouseOut={event => event.target.pause()}
src={`${vid.videos.tiny.url}#t=1`} >
</video>
You can use ref for this,
let vidRef = React.createRef();
You should create function separately,
const playVideo = () => {
// You can use the play method as normal on your video ref
vidRef.current.play();
};
const pauseVideo = () => {
// Pause as well
vidRef.current.pause();
};
provide ref to video,
<video
ref = {vidRef} //Provide ref here
poster="https://i.imgur.com/Us5ckqm.jpg"
onMouseOver={() => playVideo()}
onMouseOut={() => pauseVideo()}
src={`${vid.videos.tiny.url}#t=1`} >
</video>
Demo
The following code shows a list of 10 users (list-view) and if you click on Details button of any of those users, it shows only that particular user (user-view).
import React, { useState, useEffect } from 'react'
import axios from 'axios'
const UserList = ({ id, setID }) => {
const [resources, setResources] = useState([])
const fetchResource = async () => {
const response = await axios.get(
'https://api.randomuser.me/?results=10'
)
console.log(response.data.results)
setResources(response.data.results)
}
useEffect(() => {
fetchResource()
}, [])
return (
<ul className='card__wrapper'>
{resources.filter(user => (id) ? user.login.uuid === id : true)
.map(item => (
<li className='card' key={item.name.first}>
<div className='card__item'>
<img className='card__image' src={item.picture.large} alt={item.name.first} />
<h2 className='card__title'>{item.name.first} {item.name.last}</h2>
{
id
?
<button
className='card__cta'
onClick={() => setID(null)}
>
Back to overview
</button>
:
<button
className='card__cta'
onClick={() => setID(item.login.uuid)}
>
Details
</button>
}
</div>
</li>
))}
</ul>
)
}
export default UserList
While this is working fine, the code inside the return which builds both the list-view and also the user-view is a bit difficult to understand (at least for me) and also makes it hard for using different CSS classes for List- and User-view.
I'd like to simplify the code so that's easier to understand by splitting it to two different returns.
Basically, saying that if the condition is true, return the user-view otherwise the list-view
How can I do that?
I would put the rendering stuff into another function, and to make what is going to be clearer I would use two returns:
import React, { useState, useEffect } from "react";
import axios from "axios";
const UserList = ({ id, setID }) => {
const [resources, setResources] = useState([]);
const fetchResource = async () => {
const response = await axios.get("https://api.randomuser.me/?results=10");
console.log(response.data.results);
setResources(response.data.results);
};
useEffect(() => {
fetchResource();
}, []);
const renderItem = (item, isLoggedIn) => {
return (
<li className="card" key={item.name.first}>
<div className="card__item">
<img className="card__image" src={item.picture.large} alt={item.name.first} />
<h2 className="card__title">
{item.name.first} {item.name.last}
</h2>
{isLoggedIn ? (
<button className="card__cta" onClick={() => setID(null)}>
Back to overview
</button>
) : (
<button className="card__cta" onClick={() => setID(item.login.uuid)}>
Details
</button>
)}
</div>
</li>
);
};
const user = resources.find(user => user.login.uuid === id);
if (user) {
return <ul className="card__wrapper">{renderItem(user, true)}</ul>;
} else {
return <ul className="card__wrapper">{resources.map(user => renderItem(user, false))}</ul>;
}
};
export default UserList;
Looks like the question asked pertains to this React hooks - OnClick show only the clicked item
Please find my comment for the above post, as I guess this particular issue can be solved as mentioned in the comment! In case it doesn't fix, let me know.