I am trying to creating some reusable components to filter arrays, but I think I am doing something wrong.
Should I be passing the handleclick all the way back up the chain of components like this?
I also cannot get the loading icon to appear in the filter button, it seems the button only re-renders after the click and filtering has been completed.
Is there a better place to store the active button, but this is the only way I could get the buttons to re-render.
var FilterButton = React.createClass({
getInitialState: function() {
return { loading: false };
},
handleClick: function() {
this.setState({ loading: true }, function() {
this.props.handleClick(this.props.filter);
});
this.setState({ loading: false });
},
render: function() {
var cx = React.addons.classSet;
var classes = cx({
'btn': true,
'btn-white': !this.props.active,
'btn-primary': this.props.active
});
var loader = <i className="fa fa-circle-o-notch fa-spin"></i>;
return (
<button className={classes} onClick={this.handleClick}>
{this.state.loading ? loader : ''} {this.props.label}
</button>
);
}
});
var FilterBar = React.createClass({
getInitialState: function() {
return { filter: 1 };
},
handleClick: function(filter) {
this.setState({ filter: filter }, function() {
this.props.handleClick(this.state.filter);
});
},
render: function() {
var filter = this.state.filter;
return (
<div className="nav">
<FilterButton handleClick={this.handleClick} active={filter == 1} filter="1" label="Pending" />
<FilterButton handleClick={this.handleClick} active={filter == 2} filter="2" label="Canceled" />
<FilterButton handleClick={this.handleClick} active={filter == 3} filter="3" label="Shipped" />
</div>
);
}
});
var OrdersView = React.createClass({
getInitialState: function () {
return {orders: [], status_id: 1};
},
componentDidMount: function() {
/* get orders here via ajax and set state*/
},
handleFilter: function(status_id) {
this.setState({ status_id: status_id });
},
render: function() {
var self = this;
var orders = $.grep(this.state.orders, function(order, i){
return order.status_id == self.state.status_id;
});
return (
<div>
<FilterBar handleClick={this.handleFilter} />
<OrderList orders={orders} />
</div>
);
}
});
Related
I'm too new to react. I'm curious about a few things. The first topic I was wondering, why the HTML tags in js?
I need to do a project. I have a method that returns json with .NET
I have a code like below. How do I update the div when I add something into it?
This .net code.
private static readonly IList<Mock.Model.Skills> _Skills;
[Route("api/Skills/add")]
[HttpPost]
public ActionResult Skills(Mock.Model.Skills Model)
{
if (ModelState.IsValid)
{
_Skills.Add(Model);
return Json("true");
}
return Json(false);
}
And, js (react) code.
var Rows = React.createClass({
render: function () {
return (
<span className="label label-info tags">{this.props.item.tag}</span>
);
}
});
var SkillsRow = React.createClass({
getInitialState: function () {
return {
items: []
}
},
componentDidMount: function () {
$.get(this.props.dataUrl, function (data) {
if (this.isMounted()) {
this.setState({
items: data
});
}
}.bind(this));
},
render: function () {
var rows = [];
this.state.items.forEach(function (item, index) {
rows.push(<Rows class="label label-info tags" key={index} item={item} />);
});
return (<span>{rows}</span>);
}
});
ReactDOM.render(
<SkillsRow dataUrl="/api/Skills" />,
document.getElementById('skills-data')
);
This works well, but do not add.
I wonder if this is the correct method?
Thank you to everyone who showed interest.
If you want to add items to api, you can call something like this:
var SkillsRow = React.createClass({
getInitialState: function() {
return {
items: [],
currentEditor: ''
}
},
componentDidMount: function() {
this.updateSkillList()
},
updateSkillList: function() {
$.get(this.props.dataUrl, function(data) {
this.setState({
items: data
})
}).bind(this)
},
handleEditorChange: function(event) {
this.setState({
currentEditor: event.target.value
})
},
handlePostForm: function() {
$.post('api/Skills/add', {skill: this.state.currentEditor}, function(data) {
this.updateSkillList()
})
},
renderSkillList: function() {
this.state.items.map(function(item, idx) {
return <Row className="label label-info tags" key={idx} item={item} />
})
},
render: function() {
return (
<div>
<span>
<input value={this.state.currentEditor} onChange={this.handleEditorChange} />
<button onClick={this.handlePostForm} />
</span>
<span>{this.renderSkillList()}</span>
</div>
)
}
})
Edited:
Now i understood the question, you code will look something like this, also you will have to fix backend code to only receive the skill name, and then create the object (you can't create a C# object in javascript)
I have an array that I am sorted and I would like to change the css class of the arrow to indicate the current sorting. I have functions for both changing the class and also sorting the array but I cannot combine them onClick because one cannot be written exclusively as a functions. How can I combine these? Here is the relevant code:
The component
var Clickable = React.createClass ({
handleClick: function() {
this.props.onClick(this.props.index);
},
render: function () {
return <img className={this.props.isActive ? 'currentArrow' : 'arrow'} onClick={this.handleClick}
src={this.props.src} />
}
});
The container
var LeaderBoard = React.createClass({
getInitialState: function() {
this.state = {
activeIndex: null
};
return {
data: loading,
};
},
handleClick: function(index) {
this.setState({activeIndex: index});
},
componentWillMount: function() {
fetch('https://fcctop100.herokuapp.com/api/fccusers/top/recent', {
method: 'get'
}).then(response => response.json()).then(data => {
this.setState({
data: data,
});
}).catch(function(error) {
console.log("error is ", error);
});
},
render: function() {
return (
<div>
<div id="Title" className="row">
<h1>freeCodeCamp Leaderboard</h1>
</div>
<div className="row">
<div className="col-md-1 col-xs-1">
<h4>#</h4>
</div>
<div className="col-md-3 col-xs-3">
<h4>Camper Name
<Clickable index={0} isActive={this.state.activeIndex===0}
////Second function works but the class is not changed
onClick={() => {this.handleClick; this.setState(this.state.data.sort(sortDescending("recent")))}}
src="https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-arrow-up-b-128.png"/>
<Clickable index={1} isActive={this.state.activeIndex===1}
///class is changed but I cannot add the sorting function
onClick ={this.handleClick}
src="https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-arrow-down-b-128.png"/>
</h4>
</div>
</div>
</div>
<div>{information}</div>
</div>
);
}
});
ReactDOM.render(<LeaderBoard />, document.getElementById('theBoard'));
I realized its a lot of code but I tried to take out the part that's not pertinent.
You can add a prop to each child that represents how that particular arrow sorts data, and then use that in the handleclick method to sort data conditionally.
Parent:
var LeaderBoard = React.createClass({
getInitialState: function() {
this.state = {
activeIndex: null
};
return {
data: 'loading',
};
},
handleClick: function(sortType, index) {
const sortedData = this.state.data.slice();
if (sortType.direction === "descending") {
sortedData.sort(sortDescending(sortType.filter));
}
// other sortTypes here
this.setState({
activeIndex: index,
data: sortedData
});
},
componentWillMount: function() {
fetch('https://fcctop100.herokuapp.com/api/fccusers/top/recent', {
method: 'get'
}).then(response => response.json()).then(data => {
this.setState({
data: data,
});
}).catch(function(error) {
console.log("error is ", error);
});
},
render: function() {
return (
<div>
<div id="Title" className="row">
<h1>freeCodeCamp Leaderboard</h1>
</div>
<div className="row">
<div className="col-md-1 col-xs-1">
<h4>#</h4>
</div>
<div className="col-md-3 col-xs-3">
<h4>Camper Name
<Clickable index={0}
isActive={this.state.activeIndex===0}
onClick={(sortType, index) => this.handleClick(sortType, index)}
sortType = {{direction: 'ascending', filter: 'recent'}}
src="https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-arrow-up-b-128.png"
/>
<Clickable index={0}
isActive={this.state.activeIndex===0}
onClick={(sortType, index) => this.handleClick(sortType, index)}
sortType = {{direction: 'descending', filter: 'recent'}}
src="https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-arrow-down-b-128.png"
/>
</h4>
</div>
</div>
</div>
<div>{information}</div>
</div>
);
}
});
Child:
var Clickable = React.createClass ({
handleClick: function() {
this.props.onClick(this.props.sortType, this.props.index);
},
render: function () {
return <img className={this.props.isActive ? 'currentArrow' : 'arrow'} onClick={this.handleClick}
src={this.props.src} />
}
});
I solved it by passing the data to the child and using and if statement in the handeClick based on the index to execute the appropriate function.
Parent:
<Clickable data={this.state.data} index={0} isActive={this.state.activeIndex===0} onClick={this.handleClick}
src="https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-arrow-up-b-128.png"/>
Child:
var Clickable = React.createClass ({
handleClick: function() {
this.props.onClick(this.props.index);
if (this.props.index ===0) {
this.setState(this.props.data.sort(sortAscending("username")));
}
else if (this.props.index ===1) {
this.setState(this.props.data.sort(sortDescending("username")));
}
else if (this.props.index ===2) {
this.setState(this.props.data.sort(sortAscending("recent")));
}
else if (this.props.index ===3) {
this.setState(this.props.data.sort(sortDescending("recent")));
}
else if (this.props.index ===4) {
this.setState(this.props.data.sort(sortAscending("alltime")));
}
else if (this.props.index ===5) {
this.setState(this.props.data.sort(sortDescending("alltime")));
}
},
render: function () {
return <img className={this.props.isActive ? 'currentArrow' : 'arrow'} onClick={this.handleClick}
src={this.props.src} />
}
});
I cannot figure out why my handleDelete function isn't running. When I click 'Delete' nothing at all happens.
In dev tools I see the value for the delete button is correct but onClick with it's attributes doesn't show up at all.
var MainContainer = React.createClass({
getInitialState: function(){
return {
name: 'JK_MNO',
friends: [], //friends is items
text: ''
}
},
handleChange: function(e){
//like the onChange function
this.setState({
text: e.target.value
});
},
handleSubmit: function(e){
e.preventDefault();
if(this.state.text !== '') {
var nextfriend = this.state.friends.concat([{
text: this.state.text, id: Date.now()
}]);
var nextText = '';
this.setState({
friends: nextfriend, text: nextText
});
}
},
handleDelete: function(e){
console.log(this.state.friends);
this.friends.splice (this.props.friend.id);
this.setState({
friends: friends
});
},
render: function(){
return (
<div>
<h3> Name: {this.state.name} </h3>
<ShowList friends={this.state.friends} />
<form onSubmit={this.handleSubmit} >
Enter Friends: <input className="friendInput" onChange={this.handleChange} value={this.state.text} />
</form>
</div>
);
}
});
var ShowList = React.createClass({
render: function() {
var createFriend = function(friend) {
return (
<li key={friend.id}>{friend.text} <button onClick={this.props.handleDelete} value={friend.id}>Delete</button> </li>
);
};
return <ul>{this.props.friends.map(createFriend.bind(this))}</ul>;
}
});
ReactDOM.render(<MainContainer />, document.getElementById('container'));
https://jsfiddle.net/USERALPHA32/bdc9trux/
Very close! You just need to pass your delete function down to ShowList as a prop:
Current:
<ShowList friends={this.state.friends} />
Suggested:
<ShowList friends={this.state.friends} handleDelete={this.handleDelete} />
So, I believe this is a formatting issue OR I'm not clear about how the return works when dynamically building.
The render function in Results works, if I replace the code with anythign else it renders where I want. Similarly, the console.log's in the Results function outputs the data correctly. There's no error, it just doesn't render the html and it doesn't hit the debugger in SynonymElement.
What am I missing in here / what core concept am I misconstruing?
(This is just an input form that takes a word, user hits submit, it returns an object with the word as a key and the value an array of synonynms. that get rendered in the ul)
'use strict'
const Smithy = React.createClass({
dsiplayName: "Smithy",
getInitialState: function() {
return { data: []};
},
handleSubmit: function(data) {
$.get('/get-synonyms', { data: data.data }).done(function(data) {
this.setState({ data: data})
}.bind(this));
},
render: function() {
return (
<div className="smithy">
<h1>Craft Tweet</h1>
<SmithyForm onSubmit={this.handleSubmit} />
<Results data={this.state.data} />
</div>
)
}
})
const SmithyForm = React.createClass({
displayName: "SmithyForm",
getInitialState: function() {
return { placeholder: "tweet", value: "" };
},
handleChange: function(event) {
this.setState({value: event.target.value});
},
handleSubmit: function(event) {
event.preventDefault();
var tweet = this.state.value.trim();
this.props.onSubmit({ data: tweet });
this.setState({value: ''});
},
render: function() {
var placeholder = this.state.placeholder;
var value = this.state.value;
return (
<form className="smithyForm" onSubmit={this.handleSubmit}>
<input type="text" placeholder={placeholder} value={value} onChange={this.handleChange} />
<button>smithy</button>
</form>
);
}
})
const SynonymElement = React.createClass({
render: function() {
debugger
return (
<li>{this.props.data}</li>
)
}
})
const Results = React.createClass({
render: function() {
var words = this.props.data;
return (
<div className="results">
{
Object.keys(words).map(function(value) {
{ console.log(value) }
<div className={value}>
<ul>
{
words[value].map(function(syn) {
{ console.log(syn) }
return <SynonymElement data={syn} />
})
}
</ul>
</div>
})
}
</div>
)
}
})
ReactDOM.render(<Smithy />, document.getElementsByClassName('container')[0])
Might have some other complicating issues but assuming everything else is wired up correctly, you need to return the result of the function you pass into the first map (over the collection Object.keys(words)) just as you have for the later map otherwise the function is executed and nothing useful is returned.
Possibly just a dupe of loop inside React JSX
return (
<div className="results">
{
Object.keys(words).map(function(value) {
return ( // <-- this
<div className={value}>
var FilterList = React.createClass({
remove: function(item){
this.props.items = this.props.items.filter(function(itm){
return item.id !== itm.id;
});
return false;
},
render: function() {
var createItem = function(item) {
return (
<li>
<span>{item}</span>
<a href data-id="{item.id}" class="remove-filter" onClick={this.remove.bind(item)}>remove</a>
</li>
);
};
return <ul>{this.props.items.map(createItem.bind(this))}</ul>;
}
});
var FilterApp = React.createClass({
getInitialState: function() {
return {items: [], item: {
id: 0,
type: null
}};
},
onChangeType: function(e){
this.setState({
item: {
id: this.state.items[this.state.items.length],
type: e.target.value
}
});
},
handleSubmit: function(e) {
e.preventDefault();
var nextItems = this.state.items.concat([this.state.item]);
var item = {};
this.setState({items: nextItems, item: {}});
},
render: function() {
return (
<div>
<h3>Filters</h3>
<FilterList items={this.state.items} />
<form className="filter" onSubmit={this.handleSubmit}>
<fieldset>
<legend>Filter</legend>
<div className="form-grp">
<select name="type" onChange={this.onChangeType}>
<option>foo</option>
<option>bar</option>
<option>baz</option>
</select>
</div>
</fieldset>
<div className="actions">
<button>{'Add #' + (this.state.items.length + 1)}</button>
</div>
</form>
</div>
);
}
});
React.render(<FilterApp />, document.body);
I cannot seem to wrap my head around how to remove an item from the list. Probably making a ton of other bad design decisions here too, newbs.
Props on components are immutable, meaning you cannot modify them directly. In your above example if the FilterList component wants to remove an item, it would need to call a callback from the parent component.
A simplified example of this.
FilterApp passes a remove function to FilterList that is called on the onClick event. This will remove an item from the parent, update the state, then cause FilterList to re-render with the new content.
Hope this helps.
Something like the below should work. Let your root component manage state.
var FilterList = React.createClass({
render: function() {
var createItem = function(item) {
return (
<li>
<span>{item}</span>
<a href data-id="{item.id}" class="remove-filter" onClick={this.props.remove.bind(item)}>remove</a>
</li>
);
};
return <ul>{this.props.items.map(createItem.bind(this))}</ul>;
}
});
var FilterApp = React.createClass({
getInitialState: function() {
return {items: [], item: {
id: 0,
type: null
}};
},
onChangeType: function(e){
this.setState({
item: {
id: this.state.items[this.state.items.length],
type: e.target.value
}
});
},
remove: function(item, ev){
ev.preventDefault();
var items = this.state.items.filter(function(itm){
return item.id !== itm.id;
});
this.setState({ items: items });
},
handleSubmit: function(e) {
e.preventDefault();
var nextItems = this.state.items.concat([this.state.item]);
var item = {};
this.setState({items: nextItems, item: {}});
},
render: function() {
return (
<div>
<h3>Filters</h3>
<FilterList remove={this.remove} items={this.state.items} />
<form className="filter" onSubmit={this.handleSubmit}>
<fieldset>
<legend>Filter</legend>
<div className="form-grp">
<select name="type" onChange={this.onChangeType}>
<option>foo</option>
<option>bar</option>
<option>baz</option>
</select>
</div>
</fieldset>
<div className="actions">
<button>{'Add #' + (this.state.items.length + 1)}</button>
</div>
</form>
</div>
);
}
});
React.render(<FilterApp />, document.body);