This is my Add_Blog.js file
import React, { useState } from "react";
function AddBlogs() {
const ADD = () => {
return (
<div>
<label>Topic</label>
<input></input>
<label>Content</label>
<textarea></textarea>
<button type="button" onClick={deleteContent} value={state.length}> Delete</button>
</div>
);
}
const [state, setState] = useState([<ADD key="first"></ADD>]);
const addblog = () => {
setState([...state, <ADD key={state.length}></ADD>])
}
const deleteContent = (event, key) => {
setState([...state, state.splice(event.target.value, 1)]);
}
return (
<div>
<form>
<div>
<label>Add Title</label>
<input></input>
</div>
<div>
{
state
}
</div>
<div>
<button type="button" onClick={addblog}>Add another topic</button>
</div>
<input type="submit" value="publish"></input>
</form>
</div>
);
}
export default AddBlogs;
My expectation from this code is to remove component left of delete button when I press that delete button. But when I press delete button it removes that component and every components below it.
Any solution for this?
It does not work because you are using the splice method wrong. It returns the deleted value.
You can change your deleteContent function like this:
const deleteContent = (event, key) => {
let indexToRemove = Number(event.target.value);
setState((prevState) => prevState.filter((elem, index) => index !== indexToRemove));
}
That will be enough for the program to work as expected
Related
import React, { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import moment from 'moment'
import Avatar from '../../components/Avatar/Avatar'
import { useSelector, useDispatch} from 'react-redux'
import { useParams } from 'react-router-dom'
import { deleteAnswer } from '../../actions/question'
const DisplayAnswer = ( { question, handleShare } ) => {
const User = useSelector((state) => (state.currentUserReducer))
const dispatch = useDispatch()
const { id } = useParams()
const [button, setButton] = useState(false);
const handleDelete = (answerId, noOfAnswers) => {
dispatch(deleteAnswer(id, answerId, noOfAnswers-1))
}
const handleComment = (e) => {
setButton(!button)
alert(e.target.id)
}
return (
<div>
{
question.answer.map( (ans) => (
<div className="display-ans" key={ans._id}>
<p>{ans.answerBody}</p>
<div className="question-actions-user">
<div>
<button type="button" onClick={handleShare}>Share</button>
{
User?.result?._id === ans?.userId && (
<button type='button' onClick={ () => handleDelete(ans._id, question.noOfAnswers) }>Delete</button>
)
}
<div>
</div>
<button id = {ans._id} type='button' onClick = { (e) => handleComment(e) }> Add Comment </button>
{
button &&
(
<div id = {ans._id}>
<textarea rows='5' cols='30'> </textarea> <br />
<button type='button'> Post </button>
</div>
)
}
</div>
<div>
<p>answered { moment(ans.answeredOn).fromNow()}</p>
<Link to={`/Users/${ans.userId}`} className='user-link' style={{color:'#0086d8'}}>
<Avatar backgroundColor="lightgreen" px='8px' py='5px' borderRadius='4px'>{ans.userAnswered.charAt(0).toUpperCase()}</Avatar>
<div>
{ans.userAnswered}
</div>
</Link>
</div>
</div>
</div>
))
}
</div>
)
}
export default DisplayAnswer
I want to add a comment part under every answer
to do that i added a " Add Comment " button under every Answer and i have a button click on that button
and what i want is whenever the button is clicked the addcomment (textbox) should be added under it
but when i click the button the addcomment (textbox) is getting added under every answer
like if 10 answers are their then Addcommment box is getting added under every 10 answers
Currently there is only a single button state that all the mapped answers render a button for. A simple solution would be to instead store the answer id of the answer you want to add a comment for.
Example:
const DisplayAnswer = ({ question, handleShare }) => {
...
const [commentId, setCommentId] = useState(null); // <-- initially null
...
const handleComment = (e) => {
setCommentId(e.target.id); // <-- set answer id here
alert(e.target.id);
};
return (
<div>
{question.answer.map((ans) => (
<div className="display-ans" key={ans._id}>
<p>{ans.answerBody}</p>
<div className="question-actions-user">
<div>
...
<button
id={ans._id}
type="button"
onClick={handleComment}
>
Add Comment
</button>
{commentId === and._id && ( // <-- conditionally render match by id
<div id={ans._id}>
<textarea rows="5" cols="30" />
<br />
<button type="button">Post</button>
</div>
)}
</div>
...
</div>
</div>
))}
</div>
);
};
When the "Post comment" button is clicked and the entered comment is handled don't forget to also set the commentId value back to null to conditionally hide the input.
Each answer must have his own 'state' to display his own textArea, so you have to extract the code of the 'answer' in a new Answer component, and render a new component in the map method.
Each Answer will thus use a "useState" with a "[isTextAreaVisible, setIsTextAreaVisible] = useState(false);" state.
So I'm doing a list in which you can add items. When you add them you have two options:
Delete the whole list
Delete a specific item.
But for some reason the "handeDelete" button is not working. Can somebody tell me what did I write wrong in the code?
The link to CodeSandbox is:
codesandbox
import { useState } from "react";
import uuid from "react-uuid";
export default function ItemList() {
const [items, setItems] = useState({ item: "" });
const [groceryList, setGroceryList] = useState([]);
function handleChange(value, type) {
setItems((prev) => {
return { ...prev, [type]: value };
});
}
function handleSubmit(e) {
e.preventDefault();
const newItem = { ...items, id: uuid() };
setGroceryList([...groceryList, newItem]);
setItems({ item: "" });
}
function handleDelete(id) {
setGroceryList(groceryList.filter((items) => items.id !== id));
}
return (
<>
<form autoComplete="off" onSubmit={handleSubmit}>
<input
type="text"
name="item"
id="item"
value={items.item}
onChange={(e) => handleChange(e.target.value, "item")}
/>
</form>
{groceryList.map((list) => {
return (
<div key={list.id}>
<ul>
<li> {list.item}</li>
</ul>
<button onClick={(id) => handleDelete()}>Delete</button>
</div>
);
})}
<button onClick={() => setGroceryList([])}>Clear</button>
</>
);
}
Your delete button definition is wrong:
<button onClick={() => handleDelete(list.id)}>Delete</button>
the parameter you are receiving from the click event is not the id. Since you are not working with the event args itselfy you can safely ignore it. The second mistake was, that you are not passing the id itself to your handleDelete function.
For learning purposes, humor yourself and print the event to the console, while developing:
<button onClick={(evt) => {
console.log(evt)
handleDelete(list.id)
}}>
Delete
</button>
This will show you, that the parameter, that you named id (and I renamend to evt), is in fact reacts Synthetic Event: https://reactjs.org/docs/events.html
so I currently have a header component in my react js website and I want to set the title of it to an element of an array, but I cant seem to figure out how. here is my code
import Header from './components/Header';
var shoppinglist = []
var currentitem = ""
const App = () => {
function getData(val){
console.warn(val.target.value)
currentitem = val.target.value;
}
function logData() {
shoppinglist.push(currentitem)
console.log(shoppinglist)
}
function deleteItem() {
shoppinglist.pop()
console.log(shoppinglist)
console.log(shoppinglist[0])
}
return (
<div className="Container">
<Header title = "grocery list" />
<input type="text" onChange={getData}></input>
<button onClick={logData}>add item to list</button>
<button onClick={deleteItem}>delete item</button>
<div className="listItems">
<Header title = {shoppinglist[0]} />
</div>
</div>
);
}
export default App;
how would i set the header title to shoppinglist[0]?
You have to have React state in order to render your content - in your case title.
You can read more in react.js
One way of achieving what you asked in question: https://codesandbox.io/s/competent-wildflower-uvusd?file=/App.js
import "./styles.css";
import React from "react";
export default function App() {
const [title, setTitle] = React.useState(null);
const [input, setInput] = React.useState(null);
function getData(val) {
console.warn(val.target.value);
setInput(val.target.value);
}
function deleteItem() {
setTitle(null);
}
return (
<div className="Container">
<input type="text" onChange={getData}></input>
<button onClick={() => setTitle(input)}>add item to list</button>
<button onClick={deleteItem}>delete item</button>
{title && (
<div>
<p>{title}</p>
</div>
)}
</div>
);
}
If you want a dynamic component in react you have to use state-variables. Else React doesn't re-render your code. This could look like this with React Hooks:
import Header from './components/Header';
var currentitem = ""
const App = () => {
const [shoppinglist, setShoppinglist] = useState([]);
function getData(val){
console.warn(val.target.value)
currentitem = val.target.value;
}
function addItem() {
shoppinglist.push(currentitem)
setShoppinglist([...shoppinglist])
}
function deleteItem() {
shoppinglist.pop()
setShoppinglist([...shoppinglist])
}
return (
<div className="Container">
<Header title = "grocery list" />
<input type="text" onChange={getData}></input>
<button onClick={addItem}>add item to list</button>
<button onClick={deleteItem}>delete item</button>
<div className="listItems">
<Header title = {shoppinglist[0]} />
</div>
</div>
);
}
export default App;
I'm trying to remove a CSS class from a specific item when clicking on that item's button. Removing the CSS class will make a menu appear. How would I go about doing this with React? Here's the code.
import "./Homepage.css"
import React, { useState, useEffect, useRef } from "react"
// import { FontAwesomeIcon } from "#fortawesome/react-fontawesome"
// import { faArrowDown } from "#fortawesome/free-solid-svg-icons"
import { Link } from "react-router-dom"
import useFetch from "./useFetch"
import Axios from "axios"
export default function Homepage() {
const [body, setBody] = useState("")
const [sortedData, setSortedData] = useState("")
const [data, setData] = useState("")
const [css, setCss] = useState("")
const [flash, setFlash] = useState(null)
const posts = useFetch("http://localhost:5000/api/data")
const firstRender = useRef(true)
useEffect(() => {
let test = JSON.parse(window.localStorage.getItem("user"))
console.log(test)
setData(posts)
}, [posts])
useEffect(() => {
if (firstRender.current) {
firstRender.current = false
return
}
data.sort(function (a, b) {
return new Date(b.date) - new Date(a.date)
})
setSortedData(data)
}, [data])
const handleSubmit = (e) => {
e.preventDefault()
Axios.post("http://localhost:5000/api/react-create-post", { text: body }, { withCredentials: true })
.then((res) => {
setSortedData((prevArray) => [res.data.post, ...prevArray])
setFlash("Successfully created post.")
setCss("success-msg")
setBody("")
})
.catch((err) => {
setCss("error-msg")
setFlash("Field cannot be left blank.")
})
}
const handleClick = (e) => {
e.preventDefault()
e.target.parentElement.children[1]
}
return (
<div>
<center>
<div className="create-container">
<div className="posts-title">Create Post</div>
<form id="theForm" onSubmit={(e) => handleSubmit(e)}>
<textarea onChange={(e) => setBody(e.target.value)} value={`${body}`} id="theInput" className="post-input" name="text" type="text"></textarea>
<button className="submit-btn">POST</button>
</form>
</div>
<div id="postsContainer" className="posts-container">
<div className="posts-title">Latest Posts</div>
{flash ? <div className={css}>{flash}</div> : console.log()}
<div id="postInput">
{sortedData &&
sortedData.map((item) => {
return (
<div className="post-container" key={item._id}>
<Link className="a" to={`/user/${item.author}`}>
<h3 className="author">{item.author}</h3>
</Link>
<div className="date">{item.date.toLocaleString()}</div>
<div className="options-cont">
<button onClick={(e) => handleClick(e)} id="optionsBtn" className="options-btn">
<i className="fas fa-ellipsis-v"></i>
</button>
<button data-author={`${item.author}`} data-id={`${item._id}`} data-text={`${item.body}`} id="editBtn" className="edit inside-btn invisible">
Edit
</button>
<button data-author={`${item.author}`} data-id={`${item._id}`} id="deleteBtn" className="delete inside-btn invisible">
Delete
</button>
<br></br>
</div>
<p className="body-text">{item.body}</p>
</div>
)
})}
</div>
</div>
</center>
</div>
)
}
As far as I'm concerned using state as the className would remove or alter the CSS of each item in the "sortedData" array and make the menus for all items appear. I only want the menu for one of the items to appear.
As pilchard said, you probably want to make each of those its own component with its own "showing" state, or at least "showing" prop.
As far as I'm concerned using state as the className would remove or alter the CSS of each item in the "sortedData" array and make the menus for all items appear. I only want the menu for one of the items to appear.
That would be true if you used a single flag in state. But instead, use a set of flags, one flag for each menu, perhaps keyed by item._id.
Assuming you don't do the refactoring pilchard (and I) suggest:
You haven't shown us enough code for me to know whether you're using class components or function components, so I'm going to guess function components with hooks. If so, the initial state would be:
const [showing, setShowing] = useState(new Set());
Then when rendering, you'd assign the class:
<theElement className={showing.has(item._id) ? "class-if-any-to-show-it" : "class-if-any-to-not-show-it" ...
To toggle, in the button pass the ID:
<button onClick={(e) => handleClick(e, item._id)}
and then update state as appropriate:
const handleClick = (e, id) => {
e.preventDefault()
setShowing(showing => {
showing = new Set(showing);
if (showing.has(id)) {
showing.delete(id);
} else {
showing.add(id);
}
return showing;
});
};
I have an exercise where I have to make an input and a button. When I click the button, there has to be created a div/span below, which prints the text which is in input. If I change the text in input, it has to be refreshed in that div/span only when I click the button again. I tried to do it with makeDiv function, but it doesn't do anything. I made console.log(event.target.value) and it handles the text which is in input, but nothing happens then.
My code:
import {useState} from "react"
function About() {
const [initialValue,setInitialValue] = useState('')
const handleValueChange = (event) => {
console.log(event.target.value)
setInitialValue(event.target.value)
}
const makeDiv = () => {
return (<div>Value: {initialValue}</div>)
}
return(
<div>
<button onClick={makeDiv}>click me</button>
<div><input type="text" onChange={handleValueChange} /></div>
</div>
)
}
export default About
edit:
What if I wanted to make an exercise very similar to that, but now, I have to add <li>text in input</li> to <ul> each time I click the button. So when I click the button, I add one li to the list, I tried like this, but it doesn't compile:
import {useState} from "react"
function About() {
const [initialValueLastExercise, setInitialValueLastExercise] = useState([])
const [ValueLE, setValueLE] = useState([])
const handleValueChangeLE = (event) => {
console.log(event.target.value)
setInitialValueLastExercise([...initialValueLastExercise, event.target.value])
}
const showListWithText = () => {
setShouldDisplayText(true)
setValueLE(initialValueLastExercise)
}
return(
<div>
<button onClick={showListWithText}>click me to refresh the list</button>
<div><input type="text" onChange={handleValueChangeLE} /></div>
{shouldDisplayText && <div><ul>
{
for (let i =0; i<initialValueLastExercise.length; i++) {
<li>{initialValueLastExercise[i]}</li>
}
}</div></ul>}
</div>
)
}
export default About
This will refresh the value of the div on button click only as you have mentioned in the question.
import {useState} from "react"
function App() {
const [initialValue,setInitialValue] = useState('')
const [displayText, setDisplayText] = useState(false)
const [Value,setValue] = useState('')
const handleValueChange = (event) => {
setInitialValue(event.target.value)
}
const showText = () => {setDisplayText(true)
setValue(initialValue)};
return(
<div>
<button onClick={showText}>click me</button>
<div><input type="text" onChange={handleValueChange} /></div>
{displayText && <div>Value: {Value}</div>}
</div>
)
}
export default App
Solution for the Edited Question.
import {useState} from "react"
function App() {
const [initialValue,setInitialValue] = useState('')
const [displayText, setDisplayText] = useState(false)
const [Value,setValue] = useState([])
const handleValueChange = (event) => {
setInitialValue(event.target.value)
}
const showText = () => {setDisplayText(true)
setValue([...Value,initialValue])};
return(
<div>
<button onClick={showText}>click me</button>
<div><input type="text" onChange={handleValueChange} /></div>
<ul>{displayText && Value.length > 0 &&
Value.map((i) => {
return <li>Value: {i}</li>
})}</ul>
</div>
)
}
export default App
One way to do it is to create another state variable which indicates whether the div you're trying to make should be displayed and then render it conditionally. Something like
import {useState} from "react"
function About() {
const [initialValue,setInitialValue] = useState('')
const [shouldDisplayText, setShouldDisplayText] = useState(false)
const handleValueChange = (event) => {
console.log(event.target.value)
setInitialValue(event.target.value)
}
const showDivWithText = () => setShouldDisplayText(true);
return(
<div>
<button onClick={showDivWithText}>click me</button>
<div><input type="text" onChange={handleValueChange} /></div>
{shouldDisplayText && <div>Value: {initialValue}</div>}
</div>
)
}
export default About
Your approach is fundamentally wrong.
You should:
Store all the data about the component in the state
Render the output based on the state
So:
You need two state variables:
currentInputValue (because you need to store the value to display and edit in input)
selectedValue (because you need to store the value to be displayed in the div)
When onChange fires, update currentInputValue with the value of the input.
When onClick fires, update selectedValue with the current value of currentInputValue
When you return your data, include something like:
{selectedValue && <div>{selectedValue}</div>}
… to output a div containing the selected value only if there is a truthy value (the default empty string isn't truthy so the div won't be output then)
1st possibility - close to your code source
Don't forget to bind initialValue to the input and to add makeDiv content to the JSX :
return (
<div>
<button onClick={makeDiv}>click me</button>
<input type="text" onChange={handleValueChange} value={initialValue} />
{makeDiv}
</div>
)
2nd possibility - with another approach
return (
<div>
<button onClick={makeDiv}>click me</button>
<input type="text" onChange={handleValueChange} value={initialValue} />
{initialValue && <div>{initialValue}</div>}
</div>
)