I'm having trouble being able to show/hide certain elements in react. Basically, I have a dynamic list of li's, and within the li, I have an label, when you click on the li I want to hide the label and show an input. Usually with jQuery it's as easy as
$('#element').hide()
$('#element2').show()
I'm not quite understanding how to achieve this with my current layout
class EntriesTable extends React.Component {
constructor(props) {
super(props);
console.log(this.props.plannerId);
this.state = {
tasks: [],
plannerId: this.props.plannerId,
};
var state = this.state;
}
componentDidMount() {
this.getTasks(this.state.plannerId);
}
EditTask(id) {
console.log(id);
var spanEl = id + 'taskSpan';
var inputEl = id + 'taskInput';
//hide this span
//show input
$(spanEl).hide();
$(inputEl).show();
//when save
//hide input
//update task
//show span
}
updateTask(id, name) {
$.ajax({
type: "GET",
url: "/Task/Update",
data: { id: id, name: name },
contentType: "application/json; charset=utf-8",
success: function (data) {
console.log(data);
//this.setState({ tasks: data.ReturnObject, loading: false });
}.bind(this),
error: function (xhr, status, err) {
console.log(err);
}
});
}
createTask(name) {
//make api call to create planner here.
var data = {
Name: name,
PlannerId: model.plannerId,
Status: "Not Complete",
Description: "",
Affiliation: "",
Footprint: "",
Created: new Date(),
Modified: null,
};
$.ajax({
type: "POST",
url: "/Task/Add",
data: JSON.stringify(data),
contentType: "application/json; charset=utf-8",
success: function (data) {
console.log(data);
this.getTasks(model.plannerId);
}.bind(this),
error: function (xhr, status, err) {
console.log(err);
}
});
}
getTasks(id) {
this.setState({ tasks: [], loading: true });
$.ajax({
type: "GET",
url: "/Task/GetAll",
data: { id: id },
contentType: "application/json; charset=utf-8",
success: function (data) {
console.log(data);
this.setState({ tasks: data.ReturnObject, loading: false });
}.bind(this),
error: function (xhr, status, err) {
console.log(err);
}
});
}
render() {
const tasks = this.state.tasks.map((task) => {
var spanId = task.Id + "taskSpan";
var inputId = task.Id + "taskInput";
return (
<li key={task.Id} className="list-group-item" style={{minHeight: '50px'}}>
<div className="pull-left" style={{width: '50%'}}>
<span id={spanId} onClick={this.EditTask.bind(this, task.Id) }>{task.Name}</span>
<input id={inputId} type="text" style={{ display: 'none' } } />
</div>
<div className="pull-right" style={{marginTop: '-5px', width: '50%'}}>
<div className="pull-right">
<button className="btn btn-default">Add</button>
<button className="btn btn-default">Edit</button>
</div>
</div>
</li>
);
});
return (
<div className="table-wrapper">
<div className="task-container">
<h3>{this.props.rowName}</h3>
</div>
<ul id="tasksContainer">
{tasks}
<li className="list-group-item list-group-item-last"><input type="button" value="Add Task" onClick={this.addTask.bind(this)} className="btn btn-success btn-override" /></li>
</ul>
</div>
);
}
};
I did see other SO's which tell you to use a variable and then to show/hide by changing the variable, but I'm not sure if that's doable for my need, since I have a dynamic list it's not just a single element I am trying to show or hide.
class EntriesTable extends React.Component {
constructor(props) {
super(props);
console.log(this.props.plannerId);
this.state = {
editableTasks: [],
tasks: [],
plannerId: this.props.plannerId,
};
var state = this.state;
/* This isn't completely necessary but usually it is recommended you
* bind the class method in the constructor so it isn't bound on each
* render
*/
this.EditTask = this.EditTask.bind(this);
}
componentDidMount() {
this.getTasks(this.state.plannerId);
}
EditTask(id) {
/* So jQuery and react are kind of at odds with each other on fundamentals
* React is supposed to be declarative whereas jQuery is imperative. Using
* jQuery and React together is typically discouraged unless there is a real
* need for it. In React you are supposed to define how you want the UI to render
* based on some variables. You have two options available to you props and state.
* props are passed from the parent and are immutable. state is managed within that
* component and is mutable. So we have added a variable called editableTasks to your
* state that will contain an array of all editable tasks. Instead of trying to hide
* or show items here, we are simply going to add the id of now editable task to that
* array
*
*/
var nextState = this.state;
nextState.editableTasks.push(id);
this.setState(nextState);
}
updateTask(id, name) {
$.ajax({
type: "GET",
url: "/Task/Update",
data: { id: id, name: name },
contentType: "application/json; charset=utf-8",
success: function (data) {
console.log(data);
//this.setState({ tasks: data.ReturnObject, loading: false });
}.bind(this),
error: function (xhr, status, err) {
console.log(err);
}
});
}
createTask(name) {
//make api call to create planner here.
var data = {
Name: name,
PlannerId: model.plannerId,
Status: "Not Complete",
Description: "",
Affiliation: "",
Footprint: "",
Created: new Date(),
Modified: null,
};
$.ajax({
type: "POST",
url: "/Task/Add",
data: JSON.stringify(data),
contentType: "application/json; charset=utf-8",
success: function (data) {
console.log(data);
this.getTasks(model.plannerId);
}.bind(this),
error: function (xhr, status, err) {
console.log(err);
}
});
}
getTasks(id) {
this.setState({ tasks: [], loading: true });
$.ajax({
type: "GET",
url: "/Task/GetAll",
data: { id: id },
contentType: "application/json; charset=utf-8",
success: function (data) {
console.log(data);
this.setState({ tasks: data.ReturnObject, loading: false });
}.bind(this),
error: function (xhr, status, err) {
console.log(err);
}
});
}
render() {
const tasks = this.state.tasks.map((task) => {
var editable = this.state.editableTasks.filter(id => id === task.Id).length > 0;
/* Now here we are going to check whether this item is editable
* based on id. So we assign a variable that will eval to a bool
* based on whether when you filter editableTasks to see if it contains
* the current items id the length is greater than 0.
*
* Now below instead of applying some id attribute we are going to return either
* the input or the span based on whether it is editable using a ternary operation
*
*/
return (
<li key={task.Id} className="list-group-item" style={{minHeight: '50px'}}>
<div className="pull-left" style={{width: '50%'}}>
{editable ? <input type="text" /> : <span onClick={this.EditTask( task.Id)}>{task.Name}</span>}
</div>
<div className="pull-right" style={{marginTop: '-5px', width: '50%'}}>
<div className="pull-right">
<button className="btn btn-default">Add</button>
<button className="btn btn-default">Edit</button>
</div>
</div>
</li>
);
});
return (
<div className="table-wrapper">
<div className="task-container">
<h3>{this.props.rowName}</h3>
</div>
<ul id="tasksContainer">
{tasks}
<li className="list-group-item list-group-item-last"><input type="button" value="Add Task" onClick={this.addTask.bind(this)} className="btn btn-success btn-override" /></li>
</ul>
</div>
);
}
};
So the above should work for making items editable. Now it doesn't handle actually editing them or returning them to non-editable state. But this should illustrate how you should be accomplishing this the 'react-way'.
I encourage you to drop jQuery. jQuery is going to make your React code harder to manage and make it harder to embrace the react way. If you need something for ajax requests, there are plenty of smaller libraries that just as well suited (superagent is highly recommended but a quick google can lead you to many other)
Let me know if you have any other question.
To show dynamically show/hide a list of items in react implement Visible.js in your file:
import React, { Component } from 'react'
import { Link, Router } from 'react-router';
import { connect } from 'react-redux';
import { Card, CardActions, CardHeader, CardMedia, CardTitle, CardText } from 'material-ui/Card';
import { List, ListItem } from 'material-ui/List';
import Divider from 'material-ui/Divider';
import '../../../static/images/cms-img3.jpg';
import '../../../static/images/cms-img4.jpg';
import '../../../static/images/cms-img5.jpg';
import '../../../static/images/grid-list/vegetables-790022_640.jpg';
import '../../../static/images/grid-list/00-52-29-429_640.jpg';
import '../../../static/images/grid-list/burger-827309_640.jpg';
import '../../../static/images/grid-list/camera-813814_640.jpg';
import '../../../static/images/grid-list/morning-819362_640.jpg';
import '../../../static/images/grid-list/hats-829509_640.jpg';
import '../../../static/images/grid-list/honey-823614_640.jpg';
import '../../../static/images/grid-list/water-plant-821293_640.jpg';
import '../../../static/images/video.mp4';
import '../../../static/images/video123.mp4';
class VisibleData extends Component {
constructor(props) {
super(props);
this.state = {
items: [],
};
this.onTodoClick = this.onTodoClick.bind(this);
}
componentDidMount() {
fetch('http://new.anasource.com/team9/news-api/?operation=view')
.then(result => result.json()
.then(news => {
this.setState({ items: news.news });
})
);
}
componentWillMount() {
window.onpopstate = (event) => {
this.componentDidMount();
};
}
onTodoClick(id) {
this.setState({
items: this.state.items.filter(item => item.news_id == id)
});
}
render() {
return (
<Data show={this.onTodoClick} items={this.state.items} />
)
}
}
class Data extends Component {
onTodoClick(e, id) {
this.props.show(id);
}
render() {
return (
<div>
{this.props.items.map(item => {
const p = item.news_type == "image";
const r = item.news_type == "video";
return <Link to={"todo/#/" + item.news_id} key={item.news_id}>
<Card onClick={(e) => this.onTodoClick(e, item.news_id)} style={{margin:15}}>
<CardTitle title={item.news_title} subtitle={item.news_description}>
<CardMedia>
{p
? <img src={item.news_src_url} />
: null
}
</CardMedia>
<CardMedia>
{r
? <video controls><source src={item.news_src_url} type="video/mp4"/></video>
: null
}
</CardMedia>
<div className='date'>{item.news_created_date}</div>
</CardTitle>
</Card>
</Link>
})
}
</div>
)
}
}
export default VisibleData;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With