How to disable buttons on ReactJS properly? - javascript

I am learning ReactJS. I wrote a web app that should show an input text and a button 'Confirm', and when pressed it shows a second input text with a button 'Go back' which removes the new input. When the second one is showed, the first input and the first button are disabled.
However, my current script is not accepted. I'm having problems with passing functions as prop to the function FormRender and with indicating in the FormRender object itself the property "disabled" (it claims to have found "unexpected token").
Are there any better approaches?
function FormRender (props) {
return (
<div>
<input type = "text" placeholder = {this.props.holder} {this.props.disable} />
<button onClick = {this.props.Click()} {this.props.disable} > {this.state.value} </button>
</div>
);
};
var R = React.createClass ({
getInitialState: function () {
return { names: [], disable: false }
},
update: function () {
return this.state.disable ? "disabled" : "";
},
componentDidMount: function () {
var a = this.state.names;
a.push ( <FormRender holder = 'Name' value = 'Confirm' Click = {this.In}, disable: {this.update} /> );
this.setState ( { names: a } );
this.forceUpdate();
},
In: function () {
var a = this.state.names;
this.setState ( { disable: true } );
a.push ( <FormRender holder = 'Surname ' value = 'Back' Click = {this.Out} disable: "" /> );
this.setState ( { names: a } );
this.forceUpdate();
},
Out: function () {
var a = this.state.names;
this.setState ( { disable: false } );
a.splice(a.length-1,1);
this.setState ( { names: a } );
this.forceUpdate();
},
render: function () {
return (
<div>
{this.state.names}
</div>
);
}
});

I am not sure if it is what you want my example
function FormRender (props) {
return (
<div>
<input
type="text"
placeholder={props.holder}
disabled={props.disable()}
/>
<button
onClick={props.click}
disabled={props.disable()}
>
{props.value}
</button>
</div>
);
};
var R = React.createClass ({
getInitialState: function () {
return {names: [], disable: false}
},
update: function () {
return this.state.disable;
},
componentDidMount: function () {
var a = this.state.names;
var form = {
holder: 'Name',
value: 'Confirm',
click: this.In,
disable: this.update
};
a.push(form);
this.setState({names: a});
},
In: function () {
if (this.state.names.length > 1) {
return;
}
var a = this.state.names;
var form = {
holder: 'Surname',
value: 'Back',
click: this.Out,
disable: function () {
return false
}
}
this.setState({disable: true});
a.push(form);
this.setState({names: a});
},
Out: function () {
var a = this.state.names;
this.setState({disable: false});
a.splice(a.length-1,1);
this.setState({names: a});
},
render: function () {
return (
<div>
{this.state.names.map(function (el) {
return (<FormRender
holder={el.holder}
value={el.value}
click={el.click}
disable={el.disable}
/>);
})}
</div>
);
}
});
https://jsfiddle.net/69z2wepo/72509/
You have got many syntax errors in you code.
I recommend use linter :)

Related

ReactJS - Uncaught TypeError : Cannot read property ''__reactInternalInstance$npc92gvg9bpy20iee3fpqfr'' of null(...)

I am comparatively very new to react and starting off with some tutorials I am stuck at this point where I am unable to determine why cannot read property ''__reactInternalInstance$npc92gvg9bpy20iee3fpqfr' of null error is thrown. Here is my code:
var Profile = React.createClass({
getInitialState: function() {
return {
count: 0,
task: '',
gists: []
};
},
addTask: function(e) {
var items = this.state.items.concat([this.state.task]);
var task = ''
this.setState({ items, task });
e.preventDefault();
},
onChange: function(e) {
var task = e.target.value;
this.setState({ task });
},
getDefaultProps: function() {
return {
path: 'http://lh3.googleusercontent.com/-CdI8n3eKqBY/AAAAAAAAAAI/AAAAAAAAAAA/AKTaeK-Iti8fjN610AAfj53Twp1R8PMNiQ/s96-c-mo/photo.jpg',
items: []
};
},
increment: function() {
var count = this.state.count + 1;
this.setState({ count });
},
decrement: function() {
var count = this.state.count - 1;
if(count < 0) {
count = 0
}
this.setState({ count });
},
render: function() {
var displayTask = (task) => <li>{ task }</li>;
var newGist = function(gist) {
return <Gist username={gist.username} url={gist.url} />
};
return (
<div>
<h1>Counter : { this.state.count }</h1>
<button onClick = { this.increment }>+1</button>
<button onClick = { this.decrement }>-1</button>
<a href={ this.props.path }><img src={ this.props.path } /></a>
<h1>My Tasks</h1>
<Profile items={this.state.items} />
<form onSubmit={this.addTask}>
<input type="text" placeholder="Enter New Task" onChange={this.onChange} value={this.state.task} />
<button>Add Task</button>
</form>
<ul>
{ this.props.items.map(displayTask) }
</ul>
<h1>GistBox</h1>
{ this.state.gists.map(newGist) }
{this.props.usernames}'s' last Gist is <a href={this.props.url}>here</a>.
</div>
);
}
});
ReactDOM.render(<Profile />, document.body);

Filtering Todo list in React Js

I am new to React (I'm used to working with Angular) and I am currently working on filtering my Todo list app based on a category selection.
I cloned the Todo list app from http://todomvc.com/examples/react/#/ . I added a 'category' input, which works, but now I am trying to filter by category once the list is shown.
I currently don't have any search function for categories and am looking for some guidance as to where to start. I'll post the code below but here is the link to my repo if you want to clone it: https://github.com/aenser/todo-react
app.jsx
var app = app || {};
(function () {
'use strict';
app.ALL_TODOS = 'all';
app.ACTIVE_TODOS = 'active';
app.COMPLETED_TODOS = 'completed';
var TodoFooter = app.TodoFooter;
var TodoItem = app.TodoItem;
var ENTER_KEY = 13;
var TodoApp = React.createClass({
getInitialState: function () {
return {
nowShowing: app.ALL_TODOS,
editing: null,
newTodo: '',
newCategory: ''
};
},
componentDidMount: function () {
var setState = this.setState;
var router = Router({
'/': setState.bind(this, {nowShowing: app.ALL_TODOS}),
'/active': setState.bind(this, {nowShowing: app.ACTIVE_TODOS}),
'/completed': setState.bind(this, {nowShowing: app.COMPLETED_TODOS})
});
router.init('/');
},
handleChange: function (event) {
this.setState({newTodo: event.target.value});
},
handleCategoryChange: function (event) {
this.setState({newCategory: event.target.value});
},
handleNewTodoKeyDown: function (event) {
if (event.keyCode !== ENTER_KEY) {
return;
}
event.preventDefault();
var val = this.state.newTodo.trim();
var cat = this.state.newCategory.trim();
if (val, cat) {
this.props.model.addTodo(val, cat);
this.setState({newTodo: '', newCategory: ''});
}
},
toggleAll: function (event) {
var checked = event.target.checked;
this.props.model.toggleAll(checked);
},
toggle: function (todoToToggle) {
this.props.model.toggle(todoToToggle);
},
destroy: function (todo) {
this.props.model.destroy(todo);
},
edit: function (todo) {
this.setState({editing: todo.id});
},
save: function (todoToSave, text, cat) {
this.props.model.save(todoToSave, text, cat);
this.setState({editing: null});
},
cancel: function () {
this.setState({editing: null});
},
clearCompleted: function () {
this.props.model.clearCompleted();
},
render: function () {
var footer;
var main;
var todos = this.props.model.todos;
var shownTodos = todos.filter(function (todo) {
switch (this.state.nowShowing) {
case app.ACTIVE_TODOS:
return !todo.completed;
case app.COMPLETED_TODOS:
return todo.completed;
default:
return true;
}
}, this);
var todoItems = shownTodos.map(function (todo) {
return (
<TodoItem
key={todo.id}
todo={todo}
onToggle={this.toggle.bind(this, todo)}
onDestroy={this.destroy.bind(this, todo)}
onEdit={this.edit.bind(this, todo)}
editing={this.state.editing === todo.id}
onSave={this.save.bind(this, todo)}
onCancel={this.cancel}
/>
);
}, this);
var activeTodoCount = todos.reduce(function (accum, todo) {
return todo.completed ? accum : accum + 1;
}, 0);
var completedCount = todos.length - activeTodoCount;
if (activeTodoCount || completedCount) {
footer =
<TodoFooter
count={activeTodoCount}
completedCount={completedCount}
nowShowing={this.state.nowShowing}
onClearCompleted={this.clearCompleted}
/>;
}
if (todos.length) {
main = (
<section className="main">
<input
className="toggle-all"
type="checkbox"
onChange={this.toggleAll}
checked={activeTodoCount === 0}
/>
<ul className="todo-list">
{todoItems}
</ul>
</section>
);
}
return (
<div>
<header className="header">
<h1>todos</h1>
<form onKeyDown={this.handleNewTodoKeyDown}>
<input
placeholder="What needs to be done?"
value={this.state.newTodo}
autoFocus={true}
className="new-todo"
onChange={this.handleChange}
/>
<select value={this.state.newCategory} className="new-todo"
onChange={this.handleCategoryChange}>
<option value="">Select a Category</option>
<option value="Urgent">Urgent</option>
<option value="Soon">Soon</option>
<option value="Anytime">Anytime</option>
</select>
</form>
</header>
{main}
{footer}
</div>
);
}
});
var model = new app.TodoModel('react-todos');
function render() {
React.render(
<TodoApp model={model}/>,
document.getElementsByClassName('todoapp')[0]
);
}
model.subscribe(render);
render();
})();
todoModel.js
var app = app || {};
(function () {
'use strict';
var Utils = app.Utils;
// Generic "model" object. You can use whatever
// framework you want. For this application it
// may not even be worth separating this logic
// out, but we do this to demonstrate one way to
// separate out parts of your application.
app.TodoModel = function (key) {
this.key = key;
this.todos = Utils.store(key);
this.onChanges = [];
};
app.TodoModel.prototype.subscribe = function (onChange) {
this.onChanges.push(onChange);
};
app.TodoModel.prototype.inform = function () {
Utils.store(this.key, this.todos);
this.onChanges.forEach(function (cb) { cb(); });
};
app.TodoModel.prototype.addTodo = function (title, category) {
this.todos = this.todos.concat({
id: Utils.uuid(),
title: title,
category: category,
completed: false
});
this.inform();
};
app.TodoModel.prototype.toggleAll = function (checked) {
// Note: it's usually better to use immutable data structures since they're
// easier to reason about and React works very well with them. That's why
// we use map() and filter() everywhere instead of mutating the array or
// todo items themselves.
this.todos = this.todos.map(function (todo) {
return Utils.extend({}, todo, {completed: checked});
});
this.inform();
};
app.TodoModel.prototype.filterAll = function () {
this.todos = this.todos.map(function (todo) {
return Utils.extend({}, todo);
});
this.inform();
};
app.TodoModel.prototype.toggle = function (todoToToggle) {
this.todos = this.todos.map(function (todo) {
return todo !== todoToToggle ?
todo :
Utils.extend({}, todo, {completed: !todo.completed});
});
this.inform();
};
app.TodoModel.prototype.destroy = function (todo) {
this.todos = this.todos.filter(function (candidate) {
return candidate !== todo;
});
this.inform();
};
app.TodoModel.prototype.save = function (todoToSave, text, cat) {
this.todos = this.todos.map(function (todo) {
return todo !== todoToSave ? todo : Utils.extend({}, todo, {title: text}, {category: cat});
});
this.inform();
};
app.TodoModel.prototype.clearCompleted = function () {
this.todos = this.todos.filter(function (todo) {
return !todo.completed;
});
this.inform();
};
})();
todoItem.jsx
var app = app || {};
(function () {
'use strict';
var ESCAPE_KEY = 27;
var ENTER_KEY = 13;
app.TodoItem = React.createClass({
handleSubmit: function (event) {
var val = this.state.editText.trim();
var cat = this.state.editCategoryText.trim();
if (val || cat) {
this.props.onSave(val, cat);
this.setState({editText: this.props.todo.title, editCategoryText: this.props.todo.category});
} else {
this.props.onDestroy();
}
},
handleEdit: function (event) {
this.props.onEdit();
this.setState({editText: this.props.todo.title, editCategoryText: this.props.todo.category});
},
handleKeyDown: function (event) {
if (event.which === ESCAPE_KEY) {
this.setState({editText: this.props.todo.title});
this.props.onCancel(event);
} else if (event.which === ENTER_KEY) {
this.handleSubmit(event);
}
},
handleChange: function (event) {
if (this.props.editing) {
this.setState({editText: event.target.value});
}
},
handleCategoryChange: function (event) {
if (this.props.editing) {
this.setState({editCategoryText: event.target.value});
}
},
getInitialState: function () {
return {editText: this.props.todo.title, editCategoryText: this.props.todo.category};
},
/**
* This is a completely optional performance enhancement that you can
* implement on any React component. If you were to delete this method
* the app would still work correctly (and still be very performant!), we
* just use it as an example of how little code it takes to get an order
* of magnitude performance improvement.
*/
shouldComponentUpdate: function (nextProps, nextState) {
return (
nextProps.todo !== this.props.todo ||
nextProps.editing !== this.props.editing ||
nextState.editText !== this.state.editText ||
nextState.editCategoryText !== this.state.editCategoryText
);
},
/**
* Safely manipulate the DOM after updating the state when invoking
* `this.props.onEdit()` in the `handleEdit` method above.
* For more info refer to notes at https://facebook.github.io/react/docs/component-api.html#setstate
* and https://facebook.github.io/react/docs/component-specs.html#updating-componentdidupdate
*/
componentDidUpdate: function (prevProps) {
if (!prevProps.editing && this.props.editing) {
var node = React.findDOMNode(this.refs.editField);
node.focus();
node.setSelectionRange(node.value.length, node.value.length);
}
},
render: function () {
return (
<li className={classNames({
completed: this.props.todo.completed,
editing: this.props.editing
})}>
<div className="view">
<input
className="toggle"
type="checkbox"
checked={this.props.todo.completed}
onChange={this.props.onToggle}
/>
<label onDoubleClick={this.handleEdit}>
{this.props.todo.title}
</label>
<label onDoubleClick={this.handleEdit}>
{this.props.todo.category}
</label>
<button className="destroy" onClick={this.props.onDestroy} />
</div>
<input
ref="editField"
value={this.state.editText}
className="edit"
onChange={this.handleChange}
onKeyDown={this.handleKeyDown}
/>
<select value={this.state.EditCategoryText} className="edit" onChange={this.handleCategoryChange} defaultValue={this.props.todo.category} onKeyDown={this.handleKeyDown}>
<option value="Urgent">Urgent</option>
<option value="Soon">Soon</option>
<option value="Anytime">Anytime</option>
</select>
</li>
);
}
});
})();
Thank You for taking the time to help me figure out how to filter my search based on category selection.
Your interface is a little confusing as you seem to use the same input select for both assigning categories to todos and filtering, I'll get to that at the end of the answer, but for now, I just used the category selector for both entering data and filtering by category.
The answer to your question is extremely simple. You just filter by the category as well as by the completed state. Like this:
var shownTodos = todos.filter(function(todo) {
return(todo.category === this.state.newCategory);
}, this).filter(function (todo) {
switch (this.state.nowShowing) {
case app.ACTIVE_TODOS:
return !todo.completed;
case app.COMPLETED_TODOS:
return todo.completed;
default:
return true;
}
}, this);
I would add some more buttons along the bottom for the categorical currently on display. You would also add a new set of statuses like nowShowing for the category like nowShowingCategory. The buttons would set this to the 3 values of category, and you would use that variable in the above filter instead of newCategory from my example.

Does ReactJS have a binding for input fields?

I have a (reasonably complex) form in react that in a pared-down form looks like this:
var MyForm = React.createClass({
init: { data: { "MyValue" : "initial value" } },
getInitialState: function() {
return this.init;
},
componentDidMount: function () {
this.setState({data: this.props.basedata });
},
save: function (ev) {
ev.preventDefault();
var d = this.state.data;
console.log(ev, d, d.MyValue)
},
render: function () {
if(this.state.data === null){ return; }
var d = this.state.data;
return (
<div>
<form>
<input type="text" placeholder="Enter Value" defaultValue={d.MyValue} />
<button onClick={this.save}>Save</button>
</form>
<div>{d.MyValue}</div>
</div>);
}
});
var dataObject = { "MyValue" : "external value" }
ReactDOM.render( <MyForm basedata={dataObject} />, document.getElementById('container'));
(available as a jsfiddle here: https://jsfiddle.net/achesser/d8veuvnh/1/)
Does react have a "built in" way of ensuring that the value of d.MyValue is updated to the value within the input box at the time I click "save"? or do I have to write an OnChange handler for each input?
I've update the jsfiddle , I bound {d.MyValue} to the html value property in stead of the defaultValue:
var Hello = React.createClass({
init: { data: { "MyValue" : "initial value" } },
getInitialState: function() {
return this.init;
},
componentDidMount: function () {
this.setState({data: this.props.basedata });
},
save: function (ev) {
ev.preventDefault();
var d = this.state.data;
console.log(ev, d, d.MyValue)
},
render: function () {
if(this.state.data === null){ return; }
var d = this.state.data;
return (
<div>
<form>
<input type="text" placeholder="Enter Value" value={d.MyValue} />
<button onClick={this.save}>Save</button>
</form>
<div>{d.MyValue}</div>
</div>);
}
});
var dataObject = { "MyValue" : "external value" }
ReactDOM.render( <Hello basedata={dataObject} />, document.getElementById('container'));

React.js modify state of parent from button

I'm just digging into Reactjs. In my crude example I would like the delete button to remove an item from the items array. How would I do this from handleClick in the Btn component? Or what is the best way to handle this?
var data= {
"items": [
{
"title": "item1"
},
{
"title": "item2"
},
{
"title": "item3"
}
]
};
var Btn = React.createClass({
handleClick: function(e) {
console.log('clicked delete'+ this.props.id);
//how does delete button modify this.state.items in TodoApp?
},
render: function() {
return <button onClick={this.handleClick}>Delete</button>;
}
});
var TodoList = React.createClass({
render: function() {
var createItem = function(itemText, index) {
return <li key={index + itemText}>{itemText} <Btn id={index} /></li>;
};
return <div><ul>{this.props.items.map(createItem)}</ul></div>;
}
});
var TodoApp = React.createClass({
getInitialState: function() {
console.log("getInitialState");
return data;
},
onChange: function(e) {
this.setState({text: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var nextItems = this.state.items.concat([this.state.text]);
var nextText = '';
this.setState({items: nextItems, text: nextText});
},
render: function() {
return (
<div>
<h3>TODO</h3>
<TodoList items={this.state.items} />
<form onSubmit={this.handleSubmit}>
<input onChange={this.onChange} value={this.state.text} />
<button>{'Add #' + (this.state.items.length + 1)}</button>
</form>
</div>
);
}
});
React.render(<TodoApp />, document.getElementById('container'));
JSFiddle example
The Flux way would be to update your data store and trigger a render of your app, f.ex:
var deleter = function(id) {
data.items.splice(id,1)
React.render(<TodoApp />,
document.getElementById('container'));
}
[...]
handleClick: function(e) {
deleter(this.props.id)
}
Demo: http://jsfiddle.net/s01f1a4a/
By sending a callback to your Btn component. Explained further in this answer.

How to listen on events on react with plain javascript?

If you go here: http://facebook.github.io/react/index.html you will find tutorials in jsx but how do I use only js?
/** #jsx React.DOM */
var TodoList = React.createClass({
render: function() {
var createItem = function(itemText) {
return <li>{itemText}</li>;
};
return <ul>{this.props.items.map(createItem)}</ul>;
}
});
var TodoApp = React.createClass({
getInitialState: function() {
return {items: [], text: ''};
},
onChange: function(e) {
this.setState({text: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var nextItems = this.state.items.concat([this.state.text]);
var nextText = '';
this.setState({items: nextItems, text: nextText});
},
render: function() {
return (
<div>
<h3>TODO</h3>
<TodoList items={this.state.items} />
<form onSubmit={this.handleSubmit}>
<input onChange={this.onChange} value={this.state.text} />
<button>{'Add #' + (this.state.items.length + 1)}</button>
</form>
</div>
);
}
});
React.renderComponent(<TodoApp />, mountNode);
For example the above code is using a listener, how do I do that in plain javascript?
thanks
Just found this: http://facebook.github.io/react/jsx-compiler.html
/** #jsx React.DOM */
var TodoList = React.createClass({displayName: 'TodoList',
render: function() {
var createItem = function(itemText) {
return React.DOM.li(null, itemText);
};
return React.DOM.ul(null, this.props.items.map(createItem));
}
});
var TodoApp = React.createClass({displayName: 'TodoApp',
getInitialState: function() {
return {items: [], text: ''};
},
onChange: function(e) {
this.setState({text: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var nextItems = this.state.items.concat([this.state.text]);
var nextText = '';
this.setState({items: nextItems, text: nextText});
},
render: function() {
return (
React.DOM.div(null,
React.DOM.h3(null, "TODO"),
TodoList( {items:this.state.items} ),
React.DOM.form( {onSubmit:this.handleSubmit},
React.DOM.input( {onChange:this.onChange, value:this.state.text} ),
React.DOM.button(null, 'Add #' + (this.state.items.length + 1))
)
)
);
}
});
React.renderComponent(TodoApp(null ), mountNode);

Categories