Queshtion about removing an item for a map - node.js

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

Related

this code setting all post to true react js

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}>

How Can I Test an MUI ToggleButtonGroup with a userEvent?

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");
});
});

How to update my array of comments in reactjs?

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);
}
};

Push selected option on click

Im using Node.js, but i really don't know how i can do this guys, pls help
i need push selected option when user click on button, like a cart.
to before submit
const classes = useStyles();
const [codigo, setCodigo] = useState('');
const [nome, setNome] = useState('');
const [descricao, setDescricao] = useState('');
const [preco, setPreco] = useState('');
const [peso, setPeso] = useState('');
const [itemServico, setItemServico] = useState('');
const [servicos, setServicos] =useState([ ]);
useEffect(() =>{
async function loadServicos(){
const response = await api.get("/api/servicos");
console.log(response.data);
setServicos(response.data);
}
loadServicos();
},[])
how can i do this,
<Grid item xs={12} sm={12}>
<FormControl className={classes.formControl}>
<InputLabel id="demo-simple-select-label"></InputLabel>
<Select
native
labelId="demo-simple-select-label"
id="selectServico"
>
{servicos.map((opcao) => (
<option aria-label="None" value={opcao._id}>{opcao.codigo_servico}" - "{opcao.nome_servico}</option>
))}
</Select>
<Button onClick={itemServico.push(document.getElementById("selectServico"))}>Adicionar</Button>
</FormControl>
</Grid>
You need something like this. Please note that I couldn't througly test this as I don't have the full context of your app.
export default function App() {
const [servicos, setServicos] =useState([]);
const [itemServico, setItemServico] = useState([]);
const select = useEffect();
const add = () => {
if(!itemServico.includes(select.current.value))
setItemServico([...itemServico, select.current.value])
}
return (
<div className="App">
<Select
native
labelId="demo-simple-select-label"
id="selectServico"
ref={select}
>
{servicos.map((opcao) => (
<option aria-label="None" value={opcao._id}>{opcao.codigo_servico}" - "{opcao.nome_servico}</option>
))}
</Select>
<Button onClick={add}>Adicionar</Button>
</div>
);
}
We create a reference to the Select element by using the useRef hook. This will allow us to access the Select's value at any time.
We also create a function, add, that adds the selected option's value to the itemServico array. But it only does so if the value being added does not already exists in the array.
Finally, we use the onClick prop of the button to call the add function. This, in a nutshell, is what you would need.

Enabling submit button button when all inputs is filled?

Entire examples doesn't show simple solution how to keep submit button disabled until all fields is filled up in redux-form.
I tried to use this approach (TypeScript):
import * as React from 'react';
import * as reduxForm from 'redux-form';
export interface Props extends reduxForm.InjectedFormProps {}
let passedUsername = false;
let passedPassword = false;
const required = (value: string, callback: (passed: boolean) => void) => {
console.info(`PERFORM REQUIRED FIELD CHECK FOR ${value}`);
if (value && value.trim().length > 0) {
callback(true);
} else {
callback(false);
}
};
const usernameRequired = (value: string) => {
required(value, passed => { passedUsername = passed; });
};
const passwordRequired = (value: string) => {
required(value, passed => { passedPassword = passed; });
};
const isPassed = () => {
console.info(`USER PASSED: ${passedUsername}`);
console.info(`PASSWORD PASSED: ${passedPassword}`);
const result = passedUsername && passedPassword;
console.info(`PASSED: ${result}`);
return result;
};
const LoginForm = ({handleSubmit, pristine, submitting}: Props) => (
<form onSubmit={handleSubmit}>
<div>
<label>Username </label>
<reduxForm.Field
name="username"
component="input"
type="text"
validate={[usernameRequired]}
placeholder="Username"
/>
<br/>
<label>Password </label>
<reduxForm.Field
name="password"
component="input"
type="password"
validate={[passwordRequired]}
placeholder="Password"
/>
</div>
<br/>
<div>
<button type="submit" disabled={!isPassed()}>
<i className="fa fa-spinner fa-spin" style={{visibility: (submitting) ? 'visible' : 'hidden'}}/>
<strong>Login</strong>
</button>
</div>
</form>
);
export default reduxForm.reduxForm({
form: 'login'
})(LoginForm);
But this code above doesn't seems to be working. The form doesn't want to re-render even if I force it through subscribe event. It only re-render when pristine or submitting event is triggered. But if I want to re-render myself the form just ignore it. Maybe some flag I missed to re-render manually the form when I need to?
Ok, finally the solution has been found: just need to modify parameter pure in reduxForm constructor from true (default) to false
export default reduxForm.reduxForm({
form: 'login',
pure: false
})(LoginForm);
And the from will re-render whenever you need.

Resources