probably I got a little bit lost on the following task. I have an admin page in my application where you can see all the posts from the plattform. I'm requesting the posts from an api and Im displaying them on the page as a list. I Inserted two buttons to enable/disable the post by calling a function which does tag the post to be enabled/disabled.
Everything works fine, but I want to change the state of the button without reloading the page. Im passing disable parameter threw the button tag. I don't know why its not working, if I console.log the values its already changed there. Is this a proper way to use useeffect? I tried to use it but I failed using it correct.
Somebody can help please?
Simplified Code ( I removed the enable function, since its nearly the same like disable)
export default function Feed(props) {
const [postStatus, setPostStatus] = useState(props.posts)
async function disablePost(id, e){
e.preventDefault();
const userInput = { postId: id }
try{
const res = await axios.post(`${baseurl}/api/auth/admin/disable-post`, userInput, {
})
var currentPostStatus = postStatus;
currentPostStatus.map((el) => {
if(el.id === id){
el.disabled = true;
}
console.log(el.id)
});
setPostStatus(currentPostStatus)
console.log(postStatus)
}catch(error){
console.log(error)
return
}
}
return (
<>
<HeaderAdmin></HeaderAdmin>
<div>
{/* {console.log(props.userCount)} */}
<p>Alle Posts</p>
{postStatus.map((post) =>
<React.Fragment key={post.id}>
<p>{post.id}</p>
<p>{post.email}</p>
<p>{post.desc}</p>
<p>{post.disabled == true ? 'Post deaktviert' : 'Post aktiviert'}</p>
<button disabled={ post.disabled } onClick={(e) => disablePost(post.id, e)}>Post deaktivieren</button>
<button disabled={ !post.disabled } onClick={(e) => enablePost(post.id, e)}>Post aktivieren</button>
</React.Fragment>
)}
</div>
</>
);
}
Your screen can't refresh to the new version after you clicked the disable or enable.
var currentPostStatus = postStatus;
currentPostStatus.map((el) => {
if(el.id === id){
el.disabled = true;
}
});
In your code, you are only changing the internal property of postStatus, but React only cares about the reference of the object. so you need to do
setPostStatus([...currentPostStatus])
The above line create a new array. I personally believe this is something React should improve in the future, because half of the question about React in stackoverflow is talking about this :)
Related
I'm working on someone else example code and I made some changes for the sake of testing.
One thing I stumbled upon was that I made some minimal changes on the html part of a component and the changes are not beeing displayed.
import React from 'react'
const Choices = ({ handleNewActivity, handleAddActivity, name }) => {
return (
<div>
<button id="primary-btn" onClick={() => handleNewActivity()}>Noooooo thanks...</button>
<button id="success-btn" onClick={() => handleAddActivity(name)}>Sounds fun!</button>
</div>
)
}
export default Choices
Like in this component I changed the phrase "No thanks..." to "Noooooo thanks..."
and the app continues to display as "No thanks..."
image of component rendered
Maybe you guys can help me figure it out why this is happening.
If anyone would like to take a look at the code you can find it on:
https://github.com/mondadori89/deploying-fullstack-with-heroku-sample
const handleAddActivity = newActivity => {
activityService
.addActivity({
activity: newActivity,
})
.then(() => {
setActivities([...activities, {activity: newActivity}])
})
activityService
.getNewActivity()
.then(data => {
setNewActivity(data.activity)
})
}
This is your handleAddActivity.
it is required a parameter newActivity. but you are not using on Noooooo thanks... button
<button id="primary-btn" onClick={() => handleNewActivity()}>Noooooo thanks...</button>
Please add a parameter or make another activity to handle the Nooooo thanks ... event.
I have the following component.
const Feed: React.FC<FeedProps> = memo((props) => {
// ... lines skipped for brevity
return (
<div>
<IonList>
{
data.map((item) => {
return <FeedItem key={item.id!} item={item} />
})
}
</IonList>
<IonInfiniteScroll onIonInfinite={(e: CustomEvent<void>) => __handleIonInfinite(e)}>
<IonInfiniteScrollContent loadingText="Loading items" />
</IonInfiniteScroll>
</div>
);
async function __handleIonInfinite(e: CustomEvent<void>) {
const result = await __get();
if (result && result.length < environment.PAGE_SIZE) {
(e.target as HTMLIonInfiniteScrollElement).disabled = true;
}
(e.target as HTMLIonInfiniteScrollElement).complete();
}
});
Everything works fine when scrolling. I have one question though. At first, the page is empty and I need to scroll to trigger the event. I was wondering if there is a way of triggering the scroll event such that it fills the page?
I was thinking of implementing this myself but maybe it comes out of the box? Besides, it tends to be a little complex because, depending on your screen size, I would need to see how many times I should trigger this.
I'm new to React Js, so I can't find a solution to my problem by myself, please help me.
I'm working on a website with a blog page, blogs should be displayed dynamically on the page. When page loads I want it to have 4 blogs, and underneath there will be button, so when the user clicks it, React should render and display the rest of the blogs.
My code so far looks like this:
import { blogs} from "./blogs";
import { Blog} from "./Blog";
function BlogList() {
const cardComponent = blogs.slice(0,6).map((blog, i) => {
return (
<Blog
key={i}
id={blogs[i].id}
img={blogs[i].img.src}
date={blogs[i].date}
title={blogs[i].title}
img2={blogs[i].img2.src}
logoTitle={blogs[i].logoTitle}
text={blogs[i].text}
/>
);
});
return (
<div>{cardComponent}</div>
)
}`````
**This code lets me display 6 blogs when the page is loaded, what I want to do is add "Load More" button under these already loaded 6 blogs, when the user clicks the button it should render and display another 4 blogs from "blogs", and again have Load More button.** Any help will be greatly appreciated,
Thank you.
Your code shows a fixed amount of blogs (6). Instead of hardcoding the amount of visible blogs, you need to store it in a variable that you can change later. We will use useState for this. You also need to change the amount of posts based on a button press, so a button and an action is also needed.
function BlogList() {
// Starting number of visible blogs
const [visibleBlogs, setVisibleBlogs] = useState(6)
// Set the visible blogs to the current amount + 4
// eg. if there are 10 visible post, clicking again will show 14.
const handleClick = () => {
setVisibleBlogs(prevVisibleBlogs => prevVisibleBlogs + 4)
}
const cardComponent = blogs.slice(0, visibleBlogs).map((blog, i) => {
return (
<Blog
key={i}
id={blogs[i].id}
img={blogs[i].img.src}
date={blogs[i].date}
title={blogs[i].title}
img2={blogs[i].img2.src}
logoTitle={blogs[i].logoTitle}
text={blogs[i].text}
/>
);
});
return (
<div>
{cardComponent}
<button type="button" onClick={handleClick}>
See more
</button>
</div>
)
}
I hope it helps.
You can do it this way:
function BlogList() {
const [maxRange, setMaxRange] = useState(6);
const loadMore = useCallback(() => {
setMaxRange(prevRange => prevRange + 4);
},[])
const cardComponent = blogs.slice(0, maxRange).map((blog, i) => {
return (
<Blog
key={i}
id={blogs[i].id}
img={blogs[i].img.src}
date={blogs[i].date}
title={blogs[i].title}
img2={blogs[i].img2.src}
logoTitle={blogs[i].logoTitle}
text={blogs[i].text}
/>
);
});
return (
<div>
{cardComponent}
<button onClick={loadMore}>Load More</button>
</div>
)
}
So you can just maintain the maximum number of currently displayed Blogs in state and increment it when the button gets clicked.
I used useCallback so that a new function doesn't get created when the component re-renders.
I am new to web development and is trying to learn react and redux.
I am following this tutorial https://www.youtube.com/playlist?list=PLC3y8-rFHvwheJHvseC3I0HuYI2f46oAK
As I'm trying to extend what I learned,
I'm trying to list all the users (clickable),
once clicked will display (expand/collapse) all the post of the selected user (clickable again),
once post is clicked, will display (expand/collapse) all the comment on that selected post
APIs to use:
users: https://jsonplaceholder.typicode.com/users
posts: https://jsonplaceholder.typicode.com/posts?userId={user.id}
comments: https://jsonplaceholder.typicode.com/comments?postId={post.id}
Right now, I was able to list all the users and able to do the expand/collapse,
and also able to display the post of the user however, I am experiencing below problem:
If I click on user[0] it will expand and display the post of user[0] (OK).
then if I click user[1], will expand and display the post of user[1] (OK)
however upon click of user[1] it also changes the post listed on user[0] to list the post of user[1] as well (NOT OK)
here is my UserContainer.js
import React, { useEffect } from "react";
import { connect } from "react-redux";
import { fetchUsers, updateUser } from "../redux";
import PostsContainer from "./PostsContainer";
function UsersContainer({ userData, fetchUsers, updateUser }) {
useEffect(() => {
fetchUsers();
}, []);
const handleClick = event => {
//console.log(userData.users)
const indx = userData.users.findIndex(obj => obj.id == event.target.value);
//console.log(indx)
userData.users[indx].collapse = !userData.users[indx].collapse;
//console.log(userData.users[indx].collapse + " " + indx);
updateUser(userData);
};
return userData.loading ? (
<h2>loading</h2>
) : userData.error ? (
<h2>{userData.error}</h2>
) : (
<div>
<h2>User List</h2>
<div className="list-group">
{userData.users.map(user => (
<div>
<button
type="button"
className="list-group-item list-group-item-action"
key={user.id}
onClick={handleClick}
value={user.id}
>
{user.name}
</button>
{/* for update to change SampleContainer component to POST component */}
{!user.collapse && (
//<SampleContainer id={user.id} name={user.name} />
<PostsContainer id={user.id} />
)}
</div>
))}
</div>
</div>
);
}
const mapStateToProps = state => {
return {
userData: state.user
};
};
const mapDispatchToProps = dispatch => {
return {
fetchUsers: () => dispatch(fetchUsers()),
updateUser: users => dispatch(updateUser(users))
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(UsersContainer);
I don't know why stackoverflow finds my post have code that doesn't properly formatted therefore I wasn't able to put the PostContainer component.
here is the codesandbox link for complete reference of the code:
https://codesandbox.io/s/react-redux-testing-mi6ms
You are storing the posts of that particular selected user at an instance, so change the state posts of postsReducer to object to store the posts of multiple users
Please find the code sandbox here
EDIT
If you want to prevent the loading indicator for other users then, you need to store the array of ids that are currently being loaded, and remove id once the data is loaded, for that you need to update the way you are dealing with loading state of reducer from boolean to array
Please find the updated sandbox here
I have displayed some objects which contains pictures in it. When I try to change the page the written data like the name for example updates correctly on the other hand the picture stays the same
I tried placing an imageHash into the url so it updates the state.
state =
{
imageHash = Date.now()
}
return (
<div>
{mediaId.media && mediaId.media.map(photo => (
<img src={`http://127.0.0.1:8000/api/media/${photo.id}?${imageHash}`} alt={`media${photo.id}`}/>
))}
</div>
);
I need the corresponding photo to show.
I found the solution on my own.
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.id !== this.props.id) {
this.setState({
id: nextProps.id,
imageHash: nextProps.imageHash
});
}
setImmediate(() => this.loadData());
}
It checks for props changes and if there are any it immediately loads the data with the new props.