const likeHandler = (index) => {
const allData = [...urls]
const getMatchingImage = allData.find((_item, i) => i === index);
const getMatchingImageIndex = allData.indexOf(getMatchingImage)
if (index === getMatchingImageIndex) {
setLike(true);
}
console.log('key index handler', index);
console.log('key index getMatchingImage', getMatchingImage)
console.log('key index getMatchingImageIndex', getMatchingImageIndex)
}
<div className={styles.imgContainer}>
{urls.map((url, index) => (
<Box key={index}>
<div className={styles.postHeading}>
<img src='https://placehold.co/70x70' alt=''/>
<div className={styles.postUserName}>Dinesh Kumar</div>
</div>
<div className={styles.imgContainer} style={{backgroundImage: `url(${url})`}}></div>
<div className={styles.actionButtons}>
<div>
{!like ?
<AiOutlineHeart onClick={() => {likeHandler(index)}} className={`me-2`}/> :
<AiFillHeart onClick={() => {unLikeHander(index)}} className={`me-2`} />
}
<AiOutlineMessage className={`me-2`}/>
<FiSend className={`me-2`}/>
</div>
<div>
<FiBookmark/>
</div>
</div>
</Box>
))}
</div>
I am working on post like functionality when I likes a particular post it liked all post. I am consoling all information. Please have a look
I am working on post like functionality when I likes a particular post it liked all post. I am consoling all information. Please have a look
I am working on post like functionality when I likes a particular post it liked all post. I am consoling all information. Please have a look
You should declare the like property as an array of boolean and toggle the values referring to the element with index:
const [like, setLike] = useState(new Array(urls.length).fill(false));
...
const likeHandler = (index) => {
const allData = [...urls];
const getMatchingImage = allData.find((_item, i) => i === index);
const getMatchingImageIndex = allData.indexOf(getMatchingImage);
const updatedLikes = [...like];
updatedLikes[index] = true;
setLike(updatedLikes);
};
...
<div>
{!like[index] ? (
<AiOutlineHeart
onClick={() => {
likeHandler(index);
}}
className={`me-2`}
/>
) : (
<AiFillHeart
onClick={() => {
unLikeHander(index);
}}
className={`me-2`}
/>
)}
<AiOutlineMessage className={`me-2`} />
<FiSend className={`me-2`} />
</div>
It's better to use classes. instead of styles. This is such a conditional standard when you use .module.css
Instead of
<div className={styles.imgContainer}>
better this way :
<div className={classes.imgContainer}>
Related
Im having trouble configuring a remove function for my shopping-list project, the purpose of the project is to make a shopping list with a checkbox, a quantity and an item name, but there's another feature that i can't figure out how to add it, i want to a button
( ), that will remove the selected item, now, the item are mapped, which means they are in lines, if i write ("milk", "2") and then ("milk","3"), it will go line after line, like this:
milk - 2
milk - 3.
now, i want to add a delete button, next to every line that is created, that will be able to delete that line which is connected to him, im guessing i need to define an index, and the map function will do it for me, and it will be easier, but i havent found any explanation about it, so, if you can add to the code a remove button, and explain how did u do it, that would be lovely, thanks in advance guys!
import React, { useState } from 'react';
export const ShoppingListPageContainer = () => {
const [item, setItem] = useState('');
const [quantity, setQuantity] = useState('');
const [list, setList] = useState([]);
const add = () => {
const date = { item, quantity };
if (item || quantity) {
setList((ls) => [...ls, date]);
setItem('');
setQuantity('');
}
};
return (
<div>
<label>
<input
name='item'
value={item}
onChange={(e) => setItem(e.target.value)}
/>
<input
type='number'
name='quantity'
value={quantity}
onChange={(e) => setQuantity(e.target.value)}
/>
<button onClick={add}>add</button>
</label>
{list.map((a) => {
return (
<div>
<il>{a.item}</il>
<il>{' - ' + a.quantity + ' '}</il>
<input type='checkbox' />
<button />
</div>
);
})}
</div>
);
};
Steps:
create function which will accept id as parameter and delete the item in list which matches that id. (Note: id should be uniq).
For example:
const deleteItem = (id) => {
//logic delete by id from list
}
Add this button on map and bind id.
For example:
list.map((a)=><div>
<il>{a.item}</il>
<il>{" - "+ a.quantity + " "}</il>
<button onClick={deleteItem.bind(this, a.id)} />
</div>)
By this way you can delete only one item at a time.
By binding ID to function you will call function with binded id only.
I hope this will help you to progress... Best of luck!
export const ShoppingListPageContainer = () => {
const [item, setItem] = useState("");
const [quantity, setQuantity] = useState("");
const [list, setList] = useState([]);
const handleAddItem = () => {
const date = { item, quantity };
if (item || quantity) {
const newList = [...list, date]
setList(newList);
setItem("");
setQuantity("");
}
};
const handleRemoveItem = (index)=>{
const newList = list.filter((item)=>list.indexOf(item) !==index)
setList(newList)
}
return (
<div>
<label>
<input
name="item"
value={item}
onChange={(e) => setItem(e.target.value)}
/>
<input
type="number"
name="quantity"
value={quantity}
onChange={(e) => setQuantity(e.target.value)}
/>
<button onClick={handleAddItem}>add</button>
</label>
{list.map((a,i) => (
<div>
<il>{a.item}</il>
<il>{` ${a.quantity} `}</il>
<input type="checkbox" />
<button onClick={()=>{handleRemoveItem(i)}} />
</div>
))}
</div>
);
};
This may help you however if it does not please check the implementation of the filter method
https://www.w3schools.com/jsref/jsref_filter.asp
I am using MUI ToggleButtonGroup component like so:
<ToggleButtonGroup
color="primary"
value={mode}
exclusive
onChange={changeHandler}
fullWidth
className="mt-4"
>
<ToggleButton value="login">Login</ToggleButton>
<ToggleButton value="register">Register</ToggleButton>
</ToggleButtonGroup>
When clicking the 'Register' button, it works fine in the UI. I'd like to get a proper test written with React Testing Library.
Here's what I have:
setup();
heading = screen.getByRole("heading", { level: 2 });
const registerButton = screen.getByRole("button", { name: /register/i });
userEvent.click(registerButton);
expect(heading).toHaveTextContent("Register");
The crux of the issue seems to be that userEvent.click somehow doesn't call the changeHandler. Is there some type of bubbling or something that I need to concern myself with?
Here's a prettyDOM log of the relevant components:
<button
aria-pressed="false"
class="MuiButtonBase-root MuiToggleButton-root MuiToggleButton-fullWidth MuiToggleButton-sizeMedium MuiToggleButton-primary MuiToggleButtonGroup-grouped MuiToggleButtonGroup-groupedHorizontal css-j4p6el-MuiButtonBase-root-MuiToggleButton-root"
tabindex="0"
type="button"
value="register"
>
Register
<span
class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
/>
</button> <h2
class="MuiTypography-root MuiTypography-h5 css-ag7rrr-MuiTypography-root"
>
Login
</h2>
Add an id to the component:
<ToggleButton id={${data.value}}> {data.label}
test case:
describe("ToggleButtonGroupComponent onChange Testing", () => {
const onChange = vi.fn();
it("toggle group change value and new value updated and last value no more checked", () => {
const { container } = render(<ToggleButtonGroupComponent {...props} onChange={onChange} />);
// Before change selection
const allValueToggleButton = QueryAttributeById(container, "ssh") as HTMLInputElement;
expect(allValueToggleButton.value).toEqual("ssh");
// Change selection
const withValueToggleButton = QueryAttributeById(container, "http") as HTMLInputElement;
fireEvent.click(withValueToggleButton);
expect(withValueToggleButton.value).toEqual("http");
// Old value is no more checked
expect(allValueToggleButton.value).toEqual("ssh");
});
});
I extracted array from object by using results.post still it says data.map is not a function.
fetch('/allpost',{
headers:{
"Authorization":"Bearer "+localStorage.getItem("jwt")
}
}).then(res=>res.json())
.then(result=>{
console.log(result.posts)
setData(result.posts)
})
},[])
return (
<div>
{
data.map(item=>{
return(
<div className='home-container'>
<h5 className='home-name'>salil</h5>
<div className='card-image-container'>
<img className='card-image' alt="blank" src="https://images.unsplash.com/photo-1506744038136-46273834b3fb?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8M3x8d2FsbHBhcGVyJTIwbmF0dXJlfGVufDB8MHwwfHw%3D&auto=format&fit=crop&w=500&q=60" />
</div>
</div>
)
})
}
</div>
);
};
export default Home;
can someone tell me whats the error I made here?
First, when the 'useState' is declared, should define the types and assignments. Like this const [data,setData] = useState<Array<T>>([]);.
Secondly, fetch api,we should confirm what type we want, like .then(result=>{ setData(result?.posts || []); }). that mean if 'result?.posts' is underfind, we can save [].
Finally, we should process the different types of data at render
function _demo() {
return (
<div>
{data?.length === 0 && (
<div>
<span>Loading, Please Wait</span>
</div>
)}
{data?.length > 0 && data?.map((v)=> {
return (
<div>
</div>
)
)}
</div>
);
}
I have Posts and each post has comments. I created a component and fetched the post's comments. These comments are in array form. I mapped into the array and displayed the comments individually. The challenge I am having now is how to update the individual comments. I am using node.js and mongodb for the backend. I am new to all of these and wondering the way to handle this.
Everything works well when I tested via postman. I can update the comments. To update the comments, I need the comment Id of the comment I want to update. Remember, each comment sits in a post.
In my backend, the path to edit an individual comment is like this:
router.put("/posts/:id/comment/:commentId", async (req, res) =>{
//my codes
}
How to get the comment's id in react is my challenge now and how to fetch the value of each comment so that I can update it. Here are my codes:
export default function Comments() {
const PF = "http://localhost:5000/images/";
const {user} = useContext(Context)
const location = useLocation()
const path = location.pathname.split("/")[2];
const [comments, setComments] = useState([]);// to fetch the comments for individual post
const [updateComment, setUpdateComment] = useState(false);//for change edit mode
const [commentdescription2, setCommentDescription] = useState("")
I fetched the comments for individual posts here:
useEffect(() => {
const getPost = async () =>{
try{
const response = await axios.get("/posts/"+path )
setComments(response.data.comments) //array of comments
}catch(err){
console.log(err)
}
}
getPost()
}, [path])
The function that I tried to write to update the comments. I need the id of the comment to be able to update it to the database:
const handleCommentUpdate = async (id) =>{
const commentId = comments.map((singleId) => singleId._id ==id)
try{
await axios.put("/posts/"+ path + "/comment" + commentId, {
author: user._id,
commentdescription: commentdescription2,
})
window.location.replace("/")
}catch(err){
console.log(err)
}
}
I mapped into the array of comments and displayed them here:
return (
<div className="comment">
<h5 className='users-comment'>Users comments</h5>
{comments.map((singleComment, index)=>{
//console.log(singleComment)
const {author, commentdescription, _id, createdAt} = singleComment
return(
<>
{updateComment == _id ? //toggles comment into edit mode
<div className='comment-div' key={_id} >
<textarea className='comment-wrapper' type='text'
onChange={(e) => setCommentDescription(e.target.value)} value={commentdescription2}>
</textarea>
<button className='comment-btn'onClick={handleCommentUpdate(_id)}>Update Post</button>
</div>
:
<div className='displayed-comments'key={_id}>
<div className="commentPic">
<img className="user-pics" src={PF + singleComment.author.profilePicture} alt="" />
</div>
<div className="comment-info">
<div className="comment-author-div">
<div className='comment-author-date-info'>
<h4 className="comment-author">{singleComment.author.username}</h4>
<p className="comment-date">{new Date(singleComment.createdAt).toDateString()}
</p>
</div>
<div className="comment-edit-delete-div">
<i className="singlePostIcon fas fa-edit" onClick={() => setUpdateComment(_id)} ></i>
<i className="singlePostIcon far fa-trash-alt" ></i>
</div>
</div>
<p className="comment-content">{singleComment.commentdescription}</p>
</div>
</div>
}
</>
)
})}
</div>
)
Error message that I got when I tried using the function that I wrote about is :
Status Code: 404 Not Found
Is there a way out of this? Thanks
You need pass prop onClick a function. So you should update onClick like this.
onClick={() => handleCommentUpdate(_id)}
And you have the id of comment when you call handleCommentUpdate. So you just use it with you logic
const handleCommentUpdate = async (id) => {
try {
await axios.put("/posts/" + path + "/comment" + id, {
author: user._id,
commentdescription: commentdescription2,
});
window.location.replace("/");
} catch (err) {
console.log(err);
}
};
So I'm almost wrapping up my personal project, but one thing i'm stuck on is this:
I have a following system, where the user can see all other user's he is following (it's working).
I want to implement it in a way, that he has a reactstrap Card on the left, with a small number of users, and a show more button, which open's up the rest of the users.
This is what I got so far:
The screen :
[![enter image description here][1]][1]
And this is the code which is rendering the users (I got it earlier from a get method):
<div className="following">
<Card body outline color="secondary" >
<CardTitle className = "following-list"><following-list>Following list:</following-list></CardTitle>
<CardBody>
{following.length > 0 &&
following.map(usr => {
// return <p>{usr} </p>
return <div>
<Button className = "button-follow" outline color="primary" onClick = {() =>seachUserHandler(usr)}>{usr}</Button>
</div>
})
}
</CardBody>
</Card>
</div>
CSS code:
.following {
position: absolute;
background: whitesmoke; /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
top:87px;
left:2%;
width:15%;
}
.button-follow{
max-width: 100%;
max-height: 100%;
display: block;
}
.following-list {
color:#3a7bd5;
font-size: large;
}
The onClick on which Button just refers me to the followed usr profile page.
How can I implement the card in such a way that the user would see 3-5 users at the start, and then if he clicks "see more.." button he will see the total list? is it even possible with cards?
UPDATED:
For anyone who wanna use the trick that was suggested in the solution by the great guy below, here is the full working code:
import '../App.css';
import { useHistory } from "react-router-dom";
import React , {useState, useEffect} from 'react';
import {Navbar, NavbarBrand, Button, Input, Card,CardTitle, Col, CardBody} from 'reactstrap'
import { getUserPosts,search,getFollowingUsers,getFollowingUsersPosts } from '../fucntions/user_functions'
function Follow(){
let history = useHistory();
var email;
const [length, setLength] = useState(0)
const [following,setFollowing] = useState([])
const [userSearch, setUserSearch] = useState('')
const [followingPosts, setFollowingPosts] = useState([]);
const [showLess, setShowLess] = useState(false)
const [followingData, setFollowingData] = useState({
isInitial: true,
filteredList: following.slice(0,2),
completeList: following,
});
const showMoreUsers = (_evt /*: SyntheticEvent<Event> */) => {
setFollowingData({
...followingData,
filteredList: following,
isInitial: false
});
setShowLess(true)
}
const showLessUsers = (_evt /*: SyntheticEvent<Event> */) => {
setFollowingData({
...followingData,
filteredList: following.slice(0,2),
isInitial: true
});
setShowLess(false)
}
const moreUsersStyle = {
color: 'rgba(0,0,0,0.5)',
textDecoration: 'underline',
marginTop: '10px'
}
const handleSearchUser = e =>{setUserSearch(e.target.value);};
function handleBack(){
history.goBack()
}
function seachUserHandler(usr) {
var tempPosts;
let toSearch = null
if (typeof(usr) == 'undefined'){
toSearch = {
email: userSearch
}
} else {
toSearch = {
email: usr
}
}
search(toSearch).then(res =>{
if (res.data.code.code !== 0){
window.confirm(res.data.code.message)
} else {
const postsOfUser = {
email: toSearch.email
}
tempPosts = postsOfUser
getUserPosts(tempPosts).then(response =>{
localStorage.setItem('searchedUserPosts', JSON.stringify(response.data))
localStorage.setItem('searchedUser', toSearch.email)
localStorage.setItem('searchedUserName', res.data.user.first_name)
var lengthOfPosts = res.data.user.posts.length
localStorage.setItem('numPosts',lengthOfPosts)
history.push('/profile')
})
}
})
}
function getFollowing(){
const container ={
email: email
}
getFollowingUsers(container).then(res =>{
var data = res.data;
setFollowing(data)
setLength(data.length)
getFollowingPosts(data)
setFollowingData({
...followingData,
filteredList: data.slice(0,2),
completeList: following
})
})
}
function getFollowingPosts(data){
const container ={
data: data
}
getFollowingUsersPosts(container).then(res =>{
var data = res.data;
setFollowingPosts(data)
console.log(data)
})
}
useEffect(() =>{
if (localStorage.getItem("usertoken") === null) {
history.push('/errorPage')
} else {
const _email = localStorage.getItem('useremail')
email = _email
localStorage.removeItem('searchedUser') //used to delete the last profile searched
localStorage.removeItem('searchedUserPosts') // used to delete the last profile searched post from cache
getFollowing()
};
},[]);
return (
<div className="box">
<div>
<Navbar color="light" light expand="lg" className="justify-content-flex" style={{ padding: "5" }}>
<div className="header-home">
<NavbarBrand type="text">inTouch</NavbarBrand>
</div>
<div>
<Col>
<Input id="usr" name="user1" type="text" value={userSearch} placeholder="Enter user's email..." onChange={handleSearchUser}></Input>
</Col>
</div>
<div>
<Col>
<Button outline color="primary" onClick={() => seachUserHandler()}>Search</Button>
</Col>
</div>
<div>
<Col>
<NavbarBrand type="button" onClick={handleBack}>Back</NavbarBrand>
</Col>
</div>
</Navbar>
<div className="feed">
<Card body outline color="secondary" >
<CardTitle className = "following-list text-center"><following-list>Feed</following-list></CardTitle>
<CardBody className = "text-center">
Welcome to your feed! Catch up with the people you follow.
</CardBody>
</Card>
</div>
<div className="following">
<Card body outline color="secondary" >
<CardTitle className = "following-list text-center"><following-list>Following list:</following-list></CardTitle>
<CardBody className = "text-center">
{followingData.filteredList.length > 0 &&
followingData.filteredList.map(usr =>
<div>
<Button className = "button-follow"
outline
color="primary"
onClick = {() => seachUserHandler(usr)}>{usr}</Button>
</div>
)
}
{followingData.isInitial ?
<p onClick={showMoreUsers} style={moreUsersStyle}>Show all users...</p>
: null
}
{showLess ?
<p onClick={showLessUsers} style={moreUsersStyle}>Show less...</p>
: null
}
</CardBody>
</Card>
</div>
<div className="wrapper-all-posts">
{length > 0 &&
followingPosts.map(post => {
return <Card body outline color="secondary" className="card-home " >
<CardTitle>Posted at : {post.createdAt} By : {post.email}</CardTitle>
<CardBody>{post.post}</CardBody>
</Card>
})
}
</div>
</div>
</div>
)
}
export default Follow;
Stackblitz demo
The overall idea is using a filtered list in the component. Take a look at the code below.
const {followings} = props;
...
const [followingData, setFollowingData] = useState({
isInigial: true,
filteredList: followings.slice(0,6),
completeList: followings,
});
const _showMoreUsers = (_evt /*: SyntheticEvent<Event> */) => {
setFollowgingData({
...followingData,
filteredList: followingData.completeList,
isInitial: false
});
}
const moreUsersStyle = {
color: 'rgba(0,0,0,0.5)',
textDecoration: 'underline',
marginTop: '10px'
}
...
<div className="following">
<Card body outline color="secondary" >
<CardTitle className="following-list">
<following-list>Following list:</following-list>
</CardTitle>
<CardBody>
{followingData.filteredList.length > 0 &&
followingData.filteredListfollowing.map(usr =>
<div>
<Button className = "button-follow"
outline
color="primary"
onClick = {() => seachUserHandler(usr)}>{usr}</Button>
</div>
)
}
{followingData.isInitial ?
<p onClick={_showMoreUsers} style={moreUsersStyle}>Show More...</p>
: null
}
</CardBody>
</Card>
</div>