I'm trying to build a website where users can comment on a given post. i want to display comment when Button is pressed. All work fine, but i want to show/hide comment when i click comment button. I'm using comment button inside map function so all the elements are executing it. how to show/hide comment for individual element? Please Help.
render() {
return (
<div className="container">
{this.state.queries.map((item, key) => {
return (
<div key={key}>
<hr />
<div className="list-group-item list-group-item-secondary row ">
<div className="authorName">{item.name}</div>
<div>{item.description}</div>
<hr />
<div>
<button
className="btn btn-info"
data-toggle="collapse"
data-target="#demo"
onClick={() => {
return fetch("/queries/" + item._id)
.then(Response => Response.json())
.then(data => {
this.setState({ comment: data.comments });
});
}}
>
Comment
</button>
<div id="demo" className="collapse">
<br />
<form
className="commentForm"
action={"http://localhost:5000/queries/" + item._id}
method="POST"
>
<input
type="text"
className="form-control"
placeholder="Write a comment..."
name="comment"
/>
<button className="btn btn-lg btn-primary btn-block">
Post
</button>
</form>
<br />
<div>
{this.state.comment.map((commentItem, key) => {
return (
<div className="list-group-item list-group-item-success">
<span className="authorName">
{commentItem.author}
</span>
{commentItem.text}
</div>
);
})}
</div>
</div>
</div>
</div>
</div>
);
})}
</div>
);
}
JSONfrom server at 'localhost:5000/queries/'+item._id
{
"comments": [
{
"_id": "5b5eadeeb415381598bdc825",
"text": "sfsdfsd",
"__v": 0
},
{
"_id": "5b5ecbe5b415381598bdc827",
"text": "hii from alex",
"__v": 0
},
{
"_id": "5b5ecd9ed8f72736c830a311",
"text": "asdad",
"__v": 0
}
],
"_id": "5b5ea97f7fb6e02d58b80dba",
"name": "ram#gmail.com",
"description": "Is axios still in use?",
"__v": 3
}
First, I would highly recommend reading the Thinking in React part of react documentation. You are facing these problems because you are not leveraging/using the power of encapsulation that React components bring.
Root Cause: You have multiple places where you can show comments, however you are maintaining them in a single state, so one will always override for others.
From looking at your code, it looks like you may be aiming for the following structure:
Container -
- Posts
- Comments
Container would be responsible for showing all the "posts" --> this.state.queries.map functionality.
Each Post can be responsible for rendering its own comments. That way, even if you have multiple posts, each is responsible for their own comment section.
So your post component can look like:
class Post extends React.Component{
state = {
comments: []
}
render() {
let {item, key} = this.props;
return (<div key={key}>
<hr />
<div className="list-group-item list-group-item-secondary row ">
<div className="authorName">{item.name}</div>
<div>{item.description}</div>
<hr />
<div>
<button
className="btn btn-info"
data-toggle="collapse"
data-target="#demo"
onClick={() => {
return fetch("/queries/" + item._id)
.then(Response => Response.json())
.then(data => {
this.setState({ comment: data.comments });
});
}}
>
Comment
</button>
<div id="demo" className="collapse">
<br />
<form
className="commentForm"
action={"http://localhost:5000/queries/" + item._id}
method="POST"
>
<input
type="text"
className="form-control"
placeholder="Write a comment..."
name="comment"
/>
<button className="btn btn-lg btn-primary btn-block">
Post
</button>
</form>
<br />
<div>
{this.state.comment.map((commentItem, key) => {
return (
<div className="list-group-item list-group-item-success">
<span className="authorName">
{commentItem.author}
</span>
{commentItem.text}
</div>
);
})}
</div>
</div>
</div>
</div>
</div>)
}
And your Container can be:
render() {
return (
<div className="container">
{this.state.queries.map((item, key) => {
return (<Post item={item} key={key} />)
Related
i have form, inside i have this part of code:
<FieldArray name="items_list" component={renderItemList} explicitProp={id}/>
<div className="buttons mb-4 text-center">
<div className="btn btn-outline-info mt-4" type="button" onClick={() => push('items_list', undefined)}> Añadir Parametros </div>
</div>
outside of the form i have this:
const renderItemList = ({ fields, explicitProp }) => {
return (
<>
{fields.map((member, index) => (
<>
<div className="row mt-4" key={index}>
<div className="col-md-3">
<h4>{index + 1}- Valor de la Lista </h4>
</div>
<Field
className="form-control"
name={`${member}.idResponse`}
type='text'
component={renderIdResponse}
props={explicitProp + '_E_' + Number(index + 1)}
hidden
/>
<div className="col-md-3 mb-4">
<Field
className="form-control"
name={`${member}.title_item`}
type="text"
component="input"
/>
</div>
<div className="col-md-4">
<FieldArray name={`${member}.value_item`} component={renderValueItems} />
</div>
<div className="col-md-1"
onClick={() => fields.remove(index)}
style={{ cursor: 'pointer' }}
>
❌
</div>
</div>
</>
))}
</>
);
};
const renderValueItems = ({ fields, meta: { error } }) => (
<div className="row">
<div>
<button className="btn btn-outline-primary col-md-12" type="button" onClick={() => fields.push()}>Añadir Sinonimos</button>
</div>
{fields.map((valor, index) => (
<div className="mt-3" key={index}>
<div className="row">
<div className="col-md-10">
<Field
className="form-control"
name={valor}
type="text"
component="input"
label={`Sinónimo ${index + 1}`}
/>
</div>
<div className="col-md-2"
onClick={() => fields.remove(index)}
style={{ cursor: 'pointer' }}
> ❌
</div>
</div>
</div>
))}
{error && <li className="error">{error}</li>}
</div>
);
const renderIdResponse = ({props}) => (
console.log(props),
<Field name='texto' {...props} />
);
I would like to get inside the items_list array the object for every item like this:
"items_list": [
{
"idResponse": CA2_3_E_(index + 1)
"title_item": "RESPUESTA 1",
"value_item": [
"SINONIMO 1",
"SINONIMO 2"
]
}
]
but i can not achieve the idResponse. i pass the 'id' in the form like a explicitProp, and then inside the renderItemList i did try to pass directly a value, created with the explicitProp and index to augment the value.
But not works.
I did try to create i component "renderIdResponse" pass the explicitProp like a prop, and inside try to create the value for every object in the array, but i don't know how.
Some idea.
Thanks
I am new to React and recently started working on it. I know that we cannot change the components properties using the props.
I want to know how can we change the properties of Component?
Below is my code:
Courses.jsx
function Courses(){
return (
<div className="courses">
<h1>Ongoing Courses</h1>
<div className="row">
{CourseData.map((value,index)=>{
return (
<div className="col-md-3">
<Card title={value.title} completed={value.completed} content={value.content} value="Resume !" key={index} id={index} />
</div>
);
})}
</div>
</div>
);
}
Here above i am having a Array of Data named as courseData, I am mapping it on a Card component.
Card.jsx:
function Card(props){
function handleClick(){
}
return (
<div className="card">
<div className="card-body">
<h2 className="card-title">{props.title}</h2>
{props.content}
<br/>
<button className="btn btn-danger" > {props.value}</button>
</div>
</div>
);
}
the CourseData has following properties :
courseData : [{
key,
title,
completed
content}]
I simply want that when ever the button present is card gets clicked then the completed attribute of courseData changed to some different value that is passed through the props .
I have tried a lot but not able to do .
Any help regarding this will be helpful for me .
courseData.jsx:
const notes = [{
key: 1,
title: "some Text",
completed:false,
content: "some Text"
},
{
key: 2,
title: "some Text",
completed:false,
content: "some Text"
}]
export default notes;
Add CourseData to the state of the Courses component. Then add a method to adjust the data there. Pass the method throught props that will be called when clicking button in the Card component:
function Courses() {
const [courseData, setCourseData] = useState(CourseData);
const updateCourseData = (index) => {
courseData.splice(index, 1);
setCourseData(courseData);
}
return (
<div className="courses">
<h1>Ongoing Courses</h1>
<div className="row">
{courseData.map((value,index)=>{
return (
<div className="col-md-3">
<Card title={value.title} updateCourseData={updateCourseData} completed={value.completed} content={value.content} value="Resume !" key={index} id={index} />
</div>
);
})}
</div>
</div>
);
}
in the Card.jsx:
<button onClick={() => props.updateCourseData(props.id)} className="btn btn-danger" > {props.value}</button>
function Courses(){
const [coursesData, setCoursesData] = useState(CourseData)
return (
<div className="courses">
<h1>Ongoing Courses</h1>
<div className="row">
{coursesData.map((value,index)=>{
return (
<div className="col-md-3">
<Card coursesData={coursesData} setCoursesData={setCoursesData} title={value.title} completed={value.completed} content={value.content} value="Resume !" key={index} id={index} />
</div>
);
})}
</div>
</div>
);
function Card({id,title,value,content,coursesData,setCoursesData }){
function handleClick(e){
e.preventDefault()
setCoursesData(coursesData => {
const data = coursesData
data.splice(id,1,{
title: title,
completed: value,
content: content,
key: id
})
return data
})
}
return (
<div className="card">
<div className="card-body">
<h2 className="card-title">{title}</h2>
{content}
<br/>
<button onClick={handleClick} className="btn btn-danger">{value}</button>
</div>
</div>
);
I have a form in which I am generating form fields dynamically . Now Issue is fields which are not dynamically generated are getting input and those field which are dynamically generated are not taking input in fields. name='name' and name='age' fields are not getting input I cant insert data in them . while first two fields owner and description are working fine
import React from "react";
class Form extends React.Component {
state = {
cats: [{ name: "", age: "" }],
owner: "",
description: ""
};
handleChange = e => {
alert('inot');
if (["name", "age"].includes(e.target.className)) {
let cats = [...this.state.cats];
cats[e.target.dataset.id][
e.target.className
] = e.target.value.toUpperCase();
this.setState({ cats }, () => console.log(this.state.cats));
} else {
this.setState({ [e.target.name]: e.target.value.toUpperCase() });
}
};
addCat = e => {
this.setState(prevState => ({
cats: [...prevState.cats, { name: "", age: "" }]
}));
};
handleSubmit = e => {
e.preventDefault();
};
handleClick = e => document.getElementById(e.target.id).remove();
// changeOption =(e) => {
// this.setState.cats(e.target.value);
// }
render() {
let { owner, description, cats } = this.state;
return (
<form onSubmit={this.handleSubmit} onChange={this.handleChange}>
<div className='row mt-5'>
<div className='col-md-12 mb-5'>
<h3 className='text-center text-primary'>Create Products Option</h3>
</div>
<div className='col-md-3'></div>
<div className='col-md-3'>
<label htmlFor='name'>
<b>Owner</b>
</label>
<input
type='text'
className='form-control'
name='owner'
id='owner'
value={owner}
/>
</div>
<div className='col-md-3'>
<label htmlFor='description'>
<b>Description</b>
</label>
<input
type='text'
className='form-control'
name='description'
id='description'
value={description}
/>
<button
className='btn btn-success rounded-0 w-25 float-right mt-2 shadow-none'
onClick={this.addCat}
>
+
</button>
</div>
<div className='col-md-3'></div>
</div>
{cats.map((val, idx) => {
let catId = `cat-${idx}`,
ageId = `age-${idx}`;
return (
<form onSubmit={this.handleSubmit} onChange={this.handleChange}>
<div id={ageId} key={idx}>
<div className='row mt-5'>
<div className='col-md-3'></div>
<div className='col-md-3'>
<label htmlFor={catId}>
<b>{`Cat #${idx + 1}`}</b>
</label>
<input
type='text'
name='name'
data-id={idx}
id={catId}
value={cats[idx].name}
className='name form-control'
/>
</div>
<div className='col-md-3'>
<label htmlFor={ageId}>
<b>Age</b>
</label>
<input
type='text'
name='age'
data-id={idx}
id={ageId}
value={cats[idx].age}
className='age form-control'
/>
<button
className='btn btn-success rounded-0 w-25 float-right mt-2 shadow-none'
onClick={this.addCat}
>
+
</button>
<input
type='button'
name={ageId}
data-id={idx}
id={ageId}
value={"-"}
className='age float-right mt-2 mr-3 btn btn-danger w-25 rounded-0 w-25 shadow-none'
onClick={this.handleClick}
/>
</div>
<div className='col-md-3'></div>
</div>
</div>
</form>
);
})}
<div className='row mt-3'>
<div className='col-md-3'></div>
<div className='col-md-3'>
<label htmlFor='name'>
<b>Min Selection</b>
</label>
<input
type='text'
className='form-control'
name='min'
id='min'
value={""}
/>
</div>
<div className='col-md-3'>
<label htmlFor='description'>
<b>Max Selection</b>
</label>
<input
type='text'
className='form-control'
name='max'
id='max'
value={""}
/>
<input
className='float-right btn btn-success mt-3'
type='submit'
value='Submit'
/>
{/* <button className="float-right" onClick={this.addCat}>Add new cat</button> */}
</div>
<div className='col-md-3'></div>
</div>
</form>
);
}
}
export default Form;
Codesandbox here. Looks like you should be seeing some errors in your console that might provide some explanation:
Warning: Failed prop type: You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.
Detailed answer
Working Sandbox here. There are a few things wrong here:
Your handleChange function needs to change
Your function is looking at className which won't work. Your inputs return form-control name for example. Instead try using the element's name.
Updated function:
handleChange = e => {
if (["name", "age"].includes(e.target.name)) {
let cats = [...this.state.cats];
cats[e.target.dataset.id][e.target.name] = e.target.value.toUpperCase();
this.setState({ cats }, () => console.log(this.state.cats));
} else {
this.setState({ [e.target.name]: e.target.value.toUpperCase() });
}
};
Aside
Do not render <form> elements in other <form> elements.
Don't do this. You don't need another form here. Just remove that.
// Line 76-80:
{cats.map((val, idx) => {
let catId = `cat-${idx}`,
ageId = `age-${idx}`;
return (
<form onSubmit={this.handleSubmit} onChange={this.handleChange}>
// ...
When i trying to write something -> loosing focus on first onChange with redux-form ( Field Arrays ). v6.5.0
Method with render
const renderEmails = ({ fields, meta: { touched, error, submitFailed } }) => (
<div className="col-xs-12">
{fields.map((email, index) =>
<div key={index} className="fieldArray-container relative">
<div className="absolute-link left" style={indexStyle}>
{index + 1}
</div>
<button
type="button"
title="Remove rule"
className="btn btn-link absolute-link right"
style={removeButtonStyle}
onClick={() => fields.remove(index)}>
<i className="fa fa-times-circle" />
</button>
<Field
style={inputStyle}
name={`${email}.email`}
type="text"
component={Input}
/>
</div>
)}
<div className="text-center"><button type="button" className="btn btn-link" onClick={() => fields.push({})}>+ Add Employee</button></div>
{(touched || submitFailed) && error && <div className="alert alert-danger">{error}</div>}
</div>
);
Call fieldarray
<FieldArray name="employeeEmails" component={renderEmails} />
Seems like duplicate.
Using redux-form I'm losing focus after typing the first character
Need to hoist it up.
I started playing with some React/Redux + t7 (in order to avoid any sort of transpiling), for the sake of learning.
When it all started making some sense to me, I encountered this voodooish issue, where the bounded onClick function sometimes fires and sometimes doesn't (?!)
As you can see, clicking the plus button doesn't always invoke the bounded function to onClick.
I'm using the latest version of Google Chrome (v53).
What the hell?
JS
'use strict';
const store = Redux.createStore(Redux.combineReducers({
todos: (state = [], action) => {
switch(action.type) {
case 'ADD_TODO':
return state.concat([action.payload]);
default:
return [];
}
}
}));
t7.module((t7) => {
t7.assign("AddTodo", React.createClass({
addTodo() {
console.log('clicked');
return store.dispatch({
type: 'ADD_TODO',
payload: {
text: this.refs.todoText.value,
}
})
},
render() {
return t7`
<div className="row">
<div className="col-xs-4 form-group-lg">
<input className="form-control" ref="todoText"/>
</div>
<div className="col-xs-2">
<button className="btn btn-lg btn-info">
<span className="glyphicon glyphicon-plus"
onClick="${this.addTodo}"
style=${{fontSize: 'large'}}>
</span>
</button>
</div>
</div>
`;
}
}));
t7.assign("TodoList", React.createClass({
render() {
return t7`
<div className="row">
<div className="col-xs-12">
<ul>
${store.getState().todos.map((todo, i) => t7`
<li key=${i}>${todo.text}</li>
`)}
</ul>
</div>
<div>
`;
}
}));
const render = () => ReactDOM.render(
t7`
<div className="container">
<div className="jumbotron">
<h1>Todos</h1>
</div>
<AddTodo />
<TodoList />
</div>
`, document.getElementById('root')
);
store.subscribe(render);
render();
});
Your Click event works whenver your click on the glyphicon plus and not outside it. The issue is that you have placed the onClick event at the wrong place add it to the button rather than the span and it will work
render() {
return t7`
<div className="row">
<div className="col-xs-4 form-group-lg">
<input className="form-control" ref="todoText"/>
</div>
<div className="col-xs-2">
<button className="btn btn-lg btn-info" onClick="${this.addTodo}">
<span className="glyphicon glyphicon-plus"
style=${{fontSize: 'large'}}>
</span>
</button>
</div>
</div>
`;
}