I need to add the class active after clicking on the button and remove all other active classes.
Look here please: https://codepen.io/azat-io/pen/RWjyZX
var Tags = React.createClass({
setFilter: function(filter) {
this.props.onChangeFilter(filter);
},
render: function() {
return <div className="tags">
<button className="btn active" onClick={this.setFilter.bind(this, '')}>All</button>
<button className="btn" onClick={this.setFilter.bind(this, 'male')}>male</button>
<button className="btn" onClick={this.setFilter.bind(this, 'female')}>female</button>
<button className="btn" onClick={this.setFilter.bind(this, 'child')}>child</button>
<button className="btn" onClick={this.setFilter.bind(this, 'blonde')}>blonde</button>
</div>
}
});
var Kid = React.createClass({
render: function() {
return <ul>
<li>{this.props.name}</li>
</ul>
}
});
var List = React.createClass({
getInitialState: function() {
return {
filter: ''
};
},
changeFilter: function(filter) {
this.setState({
filter: filter
});
},
render: function() {
var list = this.props.Data;
if (this.state.filter !== '') {
list = list.filter((i)=> i.tags.indexOf(this.state.filter) !== -1);
console.log(list);
}
list = list.map(function(Props){
return <Kid {...Props} />
});
return <div>
<h2>Kids Finder</h2>
<Tags onChangeFilter={this.changeFilter}/>
{list}
</div>
}
});
var options = {
Data: [{
name: 'Eric Cartman',
tags: ['male', 'child']
},{
name: 'Wendy Testaburger',
tags: ['female', 'child']
},{
name: 'Randy Marsh',
tags: ['male']
},{
name: 'Butters Stotch',
tags: ['male', 'blonde', 'child']
},{
name: 'Bebe Stevens',
tags: ['female', 'blonde', 'child']
}]
};
var element = React.createElement(List, options);
React.render(element, document.body);
How do I make it better?
It is simple.
take a look at this
https://codepen.io/anon/pen/mepogj?editors=001
basically you want to deal with states of your component so you check the currently active one. you will need to include
getInitialState: function(){}
//and
isActive: function(){}
check out the code on the link
this is pretty useful:
https://github.com/JedWatson/classnames
You can do stuff like
classNames('foo', 'bar'); // => 'foo bar'
classNames('foo', { bar: true }); // => 'foo bar'
classNames({ 'foo-bar': true }); // => 'foo-bar'
classNames({ 'foo-bar': false }); // => ''
classNames({ foo: true }, { bar: true }); // => 'foo bar'
classNames({ foo: true, bar: true }); // => 'foo bar'
// lots of arguments of various types
classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'
// other falsy values are just ignored
classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'
or use it like this
var btnClass = classNames('btn', this.props.className, {
'btn-pressed': this.state.isPressed,
'btn-over': !this.state.isPressed && this.state.isHovered
});
Taken from their site.
render() {
let className = 'menu';
if (this.props.isActive) {
className += ' menu-active';
}
return <span className={className}>Menu</span>
}
https://reactjs.org/docs/faq-styling.html
Since you already have <Tags> component calling a function on its parent, you do not need additional state: simply pass the filter to the <Tags> component as a prop, and use this in rendering your buttons. Like so:
Change your render function inside your <Tags> component to:
render: function() {
return <div className = "tags">
<button className = {this._checkActiveBtn('')} onClick = {this.setFilter.bind(this, '')}>All</button>
<button className = {this._checkActiveBtn('male')} onClick = {this.setFilter.bind(this, 'male')}>male</button>
<button className = {this._checkActiveBtn('female')} onClick = {this.setFilter.bind(this, 'female')}>female</button>
<button className = {this._checkActiveBtn('blonde')} onClick = {this.setFilter.bind(this, 'blonde')}>blonde</button>
</div>
},
And add a function inside <Tags>:
_checkActiveBtn: function(filterName) {
return (filterName == this.props.activeFilter) ? "btn active" : "btn";
}
And inside your <List> component, pass the filter state to the <tags> component as a prop:
return <div>
<h2>Kids Finder</h2>
<Tags filter = {this.state.filter} onChangeFilter = {this.changeFilter} />
{list}
</div>
Then it should work as intended. Codepen here (hope the link works)
you can also use pure js to accomplish this like the old ways with jquery
try this if you want a simple way
warning: this may not be the correct way to do it in react.
document.getElementById("myID").classList.add("show-example");
const activeState = (e)=>{
var id = e.target.id
const idArray = ["homeBtn","aboutBtn","servicesBtn","portfolioBtn","testmBtn","blogBtn","contactBtn"]
idArray.forEach((element)=> {
document.getElementById(element).classList.remove("active")
});
console.log(id);
document.getElementById(id).classList.add("active")
}
Related
I have a object's array of users and i'm using map to show them, each user have a option buttons that is 'edit' and 'remove' options each option have a onlclick function that set a state to show another view so the code explain itselft
class App extends React.Component {
state = {
edit: false,
remove: false
}
handleEdit = () => {
this.setState({ edit: true })
}
handleRemove = () => {
this.setState({ remove: true })
}
cancelEdit = () => {
this.setState({ edit: false })
}
cancelRemove = () => {
this.setState({ remove: false })
}
renderEditItem = () => {
const {
state: {
edit,
remove
},
cancelEdit,
cancelRemove,
handleEdit,
handleRemove
} = this
if (edit) {
return (
<div>
<span>Edit view</span>
<br/>
<button onClick={cancelEdit}>Cancel</button>
</div>
)
}
if (remove) {
return (
<div>
<span>Remove view</span>
<br/>
<button onClick={cancelRemove}>Cancel</button>
</div>
)
}
return (
<div>
<button onClick={handleEdit}>Edit</button>
<br/>
<button onClick={handleRemove}>Remove</button>
</div>
)
}
renderUsers = () => {
const {
renderEditItem
} = this
const users = [
{
id: 1,
name: 'User1'
},
{
id: 2,
name: 'User-2'
},
{
id: 3,
name: 'User-3'
}
]
return users.map((user) => {
return (
<ul key={user.id}>
<li>
<div>
<span ref='span'>{user.name}</span>
<br/>
{renderEditItem()}
</div>
</li>
</ul>
)
})
}
render () {
return (
<div>
{this.renderUsers()}
</div>
)
}
}
React.render(
<App />,
document.getElementById('app')
);
JSfiddle: Here
The issue is how can you see is, when i click on the button to set the state for edit or remove option, this will show the view for all the items,
and should be only the view that is clicked, i know the state change to true and is the same for all the items but i don't know how to set the state only for one entry any idea?
Thank you in advance.
Your problem is that the edit/remove state is singular and for the entire list. Each item in the list receives the same state here:
if (edit) {
return (
<div>
<span>Edit view</span>
<br/>
<button onClick={cancelEdit}>Cancel</button>
</div>
)
}
The single edit variable from the state is applied to each list item. If you want to individually set the edit state for each item, it will need to be kept track of with that item.
EX:
const users = [
{
id: 1,
name: 'User1',
edit: true
}]
This way each individual item will be able to tell what state it is in individually. User1 item will have an edit mode that is independent of the other users.
Then you can render something like this:
return users.map((user) => {
return (
<ul key={user.id}>
<li>
<div>
<span ref='span'>{user.name}</span>
<br/>
{user.edit ? 'EDIT MODE' : 'NOT EDIT MODE'}
</div>
</li>
</ul>
)
})
I am running through a react tutorial on tutsplus that is a bit old, and the code doesn't work as it was originally written. I actually am totally ok with this as it forces me to learn more independently, however I have spent a while on a bug that I just can't figure out. The bug consists of not being able to pass on an objects key, which prevents my program from updating the state of the correct object.
First off here is the repo if you want to run this code and see it in action: https://github.com/camerow/react-voteit
I have a child component that looks like this:
var FeedItem = React.createClass({
vote: function(newCount) {
console.log("Voting on: ", this.props, " which should have a key associated.");
this.props.onVote({
key: this.props.key,
title: this.props.title,
description: this.props.desc,
voteCount: newCount
});
},
voteUp: function() {
var count = parseInt(this.props.voteCount, 10);
var newCount = count + 1;
this.vote(newCount);
},
voteDown: function() {
var count = parseInt(this.props.voteCount, 10);
var newCount = count - 1;
this.vote(newCount);
},
render: function() {
var positiveNegativeClassName = this.props.voteCount >= 0 ?
'badge badge-success' :
'badge badge-danger';
return (
<li key={this.props.key} className="list-group-item">
<span className={positiveNegativeClassName}>{this.props.voteCount}</span>
<h4>{this.props.title}</h4>
<span>{this.props.desc}</span>
<span className="pull-right">
<button id="up" className="btn btn-sm btn-primary" onClick={this.voteUp}>↑</button>
<button id="down" className="btn btn-sm btn-primary" onClick={this.voteDown}>↓</button>
</span>
</li>
);
}
});
Now when someone hits the vote button the desired behavior is for the FeedItem.vote() method to send an object up to the main Feed component:
var FeedList = React.createClass({
render: function() {
var feedItems = this.props.items;
return (
<div className="container">
<ul className="list-group">
{feedItems.map(function(item) {
return <FeedItem key={item.key}
title={item.title}
desc={item.description}
voteCount={item.voteCount}
onVote={this.props.onVote} />
}.bind(this))}
</ul>
</div>
);
}
});
Which should pass that key on throught the parent component's onVote function:
var Feed = React.createClass({
getInitialState: function () {
var FEED_ITEMS = [
{
key: 1,
title: 'JavaScript is fun',
description: 'Lexical scoping FTW',
voteCount: 34
}, {
key: 2,
title: 'Realtime data!',
description: 'Firebase is cool',
voteCount: 49
}, {
key: 3,
title: 'Coffee makes you awake',
description: 'Drink responsibly',
voteCount: 15
}
];
return {
items: FEED_ITEMS,
formDisplayed: false
}
},
onToggleForm: function () {
this.setState({
formDisplayed: !this.state.formDisplayed
});
},
onNewItem: function (newItem) {
var newItems = this.state.items.concat([newItem]);
// console.log("Creating these items: ", newItems);
this.setState({
items: newItems,
formDisplayed: false,
key: this.state.items.length
});
},
onVote: function (newItem) {
// console.log(item);
var items = _.uniq(this.state.items);
var index = _.findIndex(items, function (feedItems) {
// Not getting the correct index.
console.log("Does ", feedItems.key, " === ", newItem.key, "?");
return feedItems.key === newItem.key;
});
var oldObj = items[index];
var newItems = _.pull(items, oldObj);
var newItems = this.state.items.concat([newItem]);
// newItems.push(item);
this.setState({
items: newItems
});
},
render: function () {
return (
<div>
<div className="container">
<ShowAddButton displayed={this.state.formDisplayed} onToggleForm={this.onToggleForm}/>
</div>
<FeedForm displayed={this.state.formDisplayed} onNewItem={this.onNewItem}/>
<br />
<br />
<FeedList items={this.state.items} onVote={this.onVote}/>
</div>
);
}
});
My logic relies on being able to reconcile the keys in the onVote function, however the key prop is not being properly passed on. So my question is, how do I pass on they key through this 'one way flow' to my parent component?
Note: Feel free to point out other problems or better design decision, or absolute stupidities. Or even that I'm asking the wrong question.
Looking forward to a nice exploration of this cool framework.
The key prop has a special meaning in React. It is not passed to the component as a prop, but is used by React to aid the reconciliation of collections. If you know d3, it works similar to the key function for selection.data(). It allows React to associate the elements of the previous tree with the elements of the next tree.
It's good that you have a key (and you need one if you pass an array of elements), but if you want to pass that value along to the component, you should another prop:
<FeedItem key={item.key} id={item.key} ... />
(and access this.props.id inside the component).
I'm trying to iterate through all the keys in a hash, but no output is returned from the loop. console.log() outputs as expected. Any idea why the JSX isn't returned and outputted correct?
var DynamicForm = React.createClass({
getInitialState: function() {
var items = {};
items[1] = { name: '', populate_at: '', same_as: '',
autocomplete_from: '', title: '' };
items[2] = { name: '', populate_at: '', same_as: '',
autocomplete_from: '', title: '' };
return { items };
},
render: function() {
return (
<div>
// {this.state.items.map(function(object, i){
// ^ This worked previously when items was an array.
{ Object.keys(this.state.items).forEach(function (key) {
console.log('key: ', key); // Returns key: 1 and key: 2
return (
<div>
<FieldName/>
<PopulateAtCheckboxes populate_at={data.populate_at} />
</div>
);
}, this)}
<button onClick={this.newFieldEntry}>Create a new field</button>
<button onClick={this.saveAndContinue}>Save and Continue</button>
</div>
);
}
Object.keys(this.state.items).forEach(function (key) {
Array.prototype.forEach() doesn't return anything - use .map() instead:
Object.keys(this.state.items).map(function (key) {
var item = this.state.items[key]
// ...
a shortcut would be:
Object.values(this.state.items).map({
name,
populate_at,
same_as,
autocomplete_from,
title
} => <div key={name}>
<FieldName/>
<PopulateAtCheckboxes populate_at={data.populate_at} />
</div>);
According to MDN "You can also do more than one single operation per case, separating them with a comma." The example below works:
var stop = false, age = 23;
age > 18 ? (
alert("1"),
alert("2")
) : (
stop = true,
alert("Sorry, you are much too young!")
);
But I can't seem to do the same in React as seen below. I expect both "Yes" and "No" buttons to be displayed, but it as it only displays the "No" button.
return (
<div className="topcoat-list">
<ul className="topcoat-list__container">
{
notes.map(function (note) {
var title = note.content.substring(0, note.content.indexOf("\n"));
title = title || note.content;
var toggleDeleteDialogs = this.state.isConfirming && note.id === notepad.selectedId;
var disableDelete = this.state.isConfirming && note.id !== notepad.selectedId;
return (
<li key={note.id} onClick={this.onSelectNote.bind(null, note.id)} className="topcoat-list__item">
{title}
{
toggleDeleteDialogs ?
(
<button key={note.id} onClick={this.deleteThisNote.bind(null, note.id)} className="half">Yes</button>,
<button className="half" onClick={this.onCancelDelete}>No</button>
) : (
<button key={note.id} onClick={this.deleteThisNote.bind(null, note.id)} className="full" disabled={disableDelete ? "disabled" : ""}>Delete Note</button>
)
}
</li>
);
}.bind(this))
}
</ul>
</div>
);
Full markup: https://jsfiddle.net/55fvpcLo/
Is my syntax off or could this be done more elegantly?
The fiddle doesn't seem to be working, but I can reproduce the behavior. Although it doesn't raise the Adjacent JSX elements must be wrapped in an enclosing tag error, I suspect that that may be the reason it doesn't work, since adjacent elements is effectively what you're trying to do.
I think the simplest solution is just to wrap the two elements in an enclosing tag rather than parentheses.
You could also return an array of JSX-components, e.g.
{
toggleDeleteDialogs ?
[<Button ... />, <Button ... />] :
<Button .../>
}
#Adam Stone is right that the problem is that there are adjacent JSX elements not wrapped in a closing tag.
That said, you asked for the most elegant way to solve the problem.
I made the following changes to your code:
Used this function to selectively hide JSX elements:
var hideIfFalse=function(boolean){
return boolean? {} : {display : 'none'};
};
which you can use like this:
<div style={hideIfFalse(toggleDeleteDialogs)} />
Separated the logic for rendering the list items into a renderChildren method:
renderChildren:function(notes,classes){
return notes.map(function (note) {
//...
Made a DeleteDialog component. It has reusable functionality with its own rendering logic, and separating it out improves code readability:
var DeleteDialog=React.createClass({
render:function(){
var classes=this.props.classes;
return <div style={hideIfFalse(this.props.toggleDeleteDialogs)}>
<button onClick={this.props.onDelete} className="half">
Yes
</button>,
<button className="half" onClick={this.props.onCancelDelete}>
No
</button>
</div>
}
});
I didn't touch the classSet logic but don't understand what it's supposed to do.
Putting it all together:
var hideIfFalse=function(boolean){
return boolean? {} : {display : 'none'};
};
var notepad = {
notes:
[
{
id: 1,
content: "Hello, world!\nBoring.\nBoring.\nBoring."
},
{
id: 2,
content: "React is awesome.\nSeriously, it's the greatest."
},
{
id: 3,
content: "Robots are pretty cool.\nRobots are awesome, until they take over."
},
{
id: 4,
content: "Monkeys.\nWho doesn't love monkeys?"
}
],
selectedId: 1
};
var DeleteDialog=React.createClass({
render:function(){
var classes=this.props.classes;
return <div style={hideIfFalse(this.props.toggleDeleteDialogs)}>
<button onClick={this.props.onDelete} className="half">
Yes
</button>,
<button className="half" onClick={this.props.onCancelDelete}>
No
</button>
</div>
}
})
var NotesList = React.createClass({
getInitialState: function() {
return {
isConfirming: false
};
},
onSelectNote: function(id) {
notepad.selectedId = id;
},
deleteThisNote: function(noteId) {
if(this.state.isConfirming) {
// actual delete functionality should be here
this.setState({isConfirming: false});
}
else {
this.setState({isConfirming: true});
}
},
onCancelDelete: function() {
this.setState({ isConfirming: false });
},
renderChildren:function(notes,classes){
return notes.map(function (note) {
var title = note.content.substring(0, note.content.indexOf("\n"));
title = title || note.content;
var toggleDeleteDialogs = this.state.isConfirming && note.id === notepad.selectedId;
var disableDelete = this.state.isConfirming && note.id !== notepad.selectedId;
return <li key={note.id}
onClick={this.onSelectNote.bind(null, note.id)}
className="topcoat-list__item">
{title}
<button key={note.id} onClick={this.deleteThisNote.bind(null, note.id)} className="full" disabled={disableDelete ? "disabled" : ""}>Delete Note</button>
<DeleteDialog
toggleDeleteDialogs={toggleDeleteDialogs}
note={note}
onDelete={this.deleteThisNote.bind(null, note.id)}
onCancelDelete={this.onCancelDelete.bind(this)} />
</li>
}.bind(this))
},
render: function() {
var notes = notepad.notes;
var cx = React.addons.classSet;
var classes = cx({
"topcoat-button-bar__button": true,
"full": !this.state.isConfirming,
"half": this.state.isConfirming,
});
return (
<div className="topcoat-list">
<ul className="topcoat-list__container">
{this.renderChildren(notes,classes)}
</ul>
</div>
);
}
});
React.render(<NotesList />, document.getElementById('container'));
JSFiddle: http://jsfiddle.net/55fvpcLo/2/
I'm having a little problem while trying to create a checkbox that selects and deselects other individual checkboxes (select/deselect all) with React. I've read http://facebook.github.io/react/docs/forms.html and discovered that there are differences between controlled and not-controlled <input>s. My test code is as follows:
var Test = React.createClass({
getInitialState: function() {
return {
data: [
{ id: 1, selected: false },
{ id: 2, selected: false },
{ id: 3, selected: false },
{ id: 4, selected: false }
]
};
},
render: function() {
var checks = this.state.data.map(function(d) {
return (
<div>
<input type="checkbox" data-id={d.id} checked={d.selected} onChange={this.__changeSelection} />
{d.id}
<br />
</div>
);
});
return (
<form>
<input type="checkbox" ref="globalSelector" onChange={this.__changeAllChecks} />Global selector
<br />
{checks}
</form>
);
},
__changeSelection: function(e) {
var id = e.target.getAttribute('data-id');
var state = this.state.data.map(function(d) {
return {
id: d.id,
selected: (d.id === id ? !d.selected : d.selected)
};
});
this.setState({ data: state });
},
__changeAllChecks: function(e) {
var value = this.refs.globalSelector.getDOMNode().checked;
var state = this.state.data.map(function(d) {
return { id: d.id, selected: value };
});
this.setState({ data: state });
}
});
React.renderComponent(<Test />, document.getElementById('content'));
The "Global selector" works as expected: when selected, all other checks are selected. The problem is that the __changeSelection() handler is not fired when one of the other checkboxes are clicked.
I don't know what is the proper way to make this work. Maybe React model is not the best one to model this kind of interaction? What could I do?
Thanks in advance
In your render function, the scope of this for the checks mapping function is different from render, which is the scope you need for __changeSelection, so this.__changeSelection won't locate a __changeSelection property. If you add a .bind(this) to the end of that mapping function, you can bind it's scope to the same this as render:
var checks = this.state.data.map(function(d) {
return (
<div>
<input type="checkbox" data-id={d.id} checked={d.selected} onChange={this.__changeSelection} />
{d.id}
<br />
</div>
);
}.bind(this));
On a side note, I would just pass the id to the handler function instead of assigning data-attributes. This will remove the need to locate that element in your handler:
var checks = this.state.data.map(function(d) {
return (
<div>
<input type="checkbox" checked={d.selected} onChange={this.__changeSelection.bind(this, d.id)} />
{d.id}
<br />
</div>
);
}.bind(this));
Then update your __changeSelection function to pass in the id as the first arg and remove the attribute lookup line:
__changeSelection: function(id) {
var state = this.state.data.map(function(d) {
return {
id: d.id,
selected: (d.id === id ? !d.selected : d.selected)
};
});
this.setState({ data: state });
}
Here is an example of it all put together, along with a jsfiddle for you to try it out:
/** #jsx React.DOM */
var Test = React.createClass({
getInitialState: function() {
return {
data: [
{ id: 1, selected: false },
{ id: 2, selected: false },
{ id: 3, selected: false },
{ id: 4, selected: false }
]
};
},
render: function() {
var checks = this.state.data.map(function(d) {
return (
<div>
<input type="checkbox" checked={d.selected} onChange={this.__changeSelection.bind(this, d.id)} />
{d.id}
<br />
</div>
);
}.bind(this));
return (
<form>
<input type="checkbox" ref="globalSelector" onChange={this.__changeAllChecks} />Global selector
<br />
{checks}
</form>
);
},
__changeSelection: function(id) {
var state = this.state.data.map(function(d) {
return {
id: d.id,
selected: (d.id === id ? !d.selected : d.selected)
};
});
this.setState({ data: state });
},
__changeAllChecks: function() {
var value = this.refs.globalSelector.getDOMNode().checked;
var state = this.state.data.map(function(d) {
return { id: d.id, selected: value };
});
this.setState({ data: state });
}
});
React.renderComponent(<Test />, document.getElementById('content'));
If you are dealing with checkboxes you can use the checkedLink attribute. Here is another possible implementation, that makes the global checkbox controlled (instead of uncontrolled in the current answers):
JsFiddle
var Test = React.createClass({
getInitialState: function() {
return {
globalCheckbox: false,
data: [
{ id: 1, selected: false },
{ id: 2, selected: false },
{ id: 3, selected: false },
{ id: 4, selected: false }
]
};
},
changeCheckForId: function(id,bool) {
this.setState(
{
data: this.state.data.map(function(d) {
var newSelected = (d.id === id ? bool : d.selected);
return {id: d.id, selected: newSelected};
}
)});
},
changeCheckForAll: function(bool) {
this.setState({
globalCheckbox: true,
data: this.state.data.map(function(d) {
return {id: d.id, selected: bool};
})
});
},
linkCheckbox: function(d) {
return {
value: d.selected,
requestChange: function(bool) { this.changeCheckForId(d.id,bool); }.bind(this)
};
},
linkGlobalCheckbox: function() {
return {
value: this.state.globalCheckbox,
requestChange: function(bool) { this.changeCheckForAll(bool); }.bind(this)
};
},
render: function() {
var checks = this.state.data.map(function(d) {
return (
<div>
<input key={d.id} type="checkbox" checkedLink={this.linkCheckbox(d)} />
{d.id}
<br />
</div>
);
}.bind(this));
return (
<form>
<input type="checkbox" checkedLink={this.linkGlobalCheckbox()} />Global selector
<br />
{checks}
</form>
);
},
});
It is simpler to use checkedLink=this.linkState("checkboxValue") with LinkedStateMixin if the state to mutate is not deeply nested (like this is the case in this question)
Edit: checkedLink and valueLink are being deprecated but were recommmended in previous versions of React.