I'm currently "testing the waters" with Reactjs. Based on their docs, I have whipped up a small project that I'm stuck with. So far, when the checkbox is checked, the state changes but....not sure how to change a state unchecked:
var Foo = React.createClass{(
getInitialState: function() {
return {
location: true,
}
},
onClick: function() {
this.setState({ location: false });
},
render: function() {
var inlineStyles = {
display: this.state.location ? 'block' : 'none'
};
return (
<div>
<input type="checkbox"
onClick={this.onClick}
/> show / hide bar
<hr />
<div style={inlineStyles}>
<p>bar</p>
</div>
</div>
);
}
)};
Do I need to use an if statement for the sort of thing I want? I need to this.setState.location: true when unchecked.
You need to read the state of the checkbox during a click, and apply that to your React state.
var Foo = React.createClass({
render: function() {
return <input type="checkbox" onClick={this.onClick} checked={!this.state.location}></input>
},
onClick: function(e) {
this.setState({location: !e.target.checked});
},
getInitialState: function() {
return {
location: true
};
}
});
Related
I'm trying to create a menu where the user should create a character details but I'm having an issue to update the Options states through an input of a child.
var Name = React.createClass({
render: function(){
return(
<input type="text"/>
)
}
});
var Options = React.createClass({
getInitialState: function(){
return{
name: ''
}
},
render: function(){
return (
<div>
Name: <Name onChange={this.updateName} value={this.state.name} />
</div>
)
},
updateName: function(evt){
this.setState({
name: evt.target.value
});
}
});
How can I go about updating the Option states using the input from Name?
you need onChange function on the Name component as well, that sends the value to the parent component
Try this:
var Name = React.createClass({
onUpdate: function(evt) {
this.props.onChange(evt);
}
render: function(){
return(
<input type="text" onChange={this.onUpdate} value={this.props.value}/>
)
}
});
var Options = React.createClass({
getInitialState: function(){
return{
name: ''
}
},
render: function(){
return (
<div>
Name: <Name onChange={this.updateName} value={this.state.name} />
</div>
)
},
updateName: function(evt){
this.setState({
name: evt.target.value
});
}
});
Component Communication in React
Refer this link to know what are the ways to communicate between React components.
My project keeps crashing when I attempt to insert a new recipe element. I use the this.state.recipes.map... in RecipeList to be able to update the recipes as needed(e.g. delete,edit,etc.). The delete functionality works, but I am unable to add a new recipe element.
If I switch the statement to this.props.recipes.map..., I am able to insert elements without an issue, but am unable to delete since the delete triggers a state change, and needs the state change to reflect the update instead of the props. Anyone have any tips on this issue? Thanks!
Recipe List:
var RecipeList = React.createClass({
getInitialState: function(){
return {recipes: []};
},
deleteRecipe: function(recipe){
var curRecipes = this.state.recipes.slice('');
curRecipes.splice(recipe.recipeKey,1);
this.setState({recipes: curRecipes});
},
componentWillMount: function(){
this.setState({recipes: this.props.recipes});
},
render: function(){
var recipeNodes = this.state.recipes.map(function(recipe,index){
return <Recipe onDelete={this.deleteRecipe} recipeKey={index} key={index} recipeTitle={recipe.recipeTitle} ingredients={recipe.ingredients} instructions={recipe.instructions} />
},this);
return(
<div>
{recipeNodes}
</div>
);
}
});
Recipe Container:
var RecipeBox = React.createClass({
getInitialState: function(){
return {showForm: false,
recipes: []
};
},
openForm: function(){
this.setState({showForm: true});
},
handleRecipeSubmit: function(recipe){
var curRecipes = this.state.recipes.slice('');
curRecipes.push({recipeTitle: recipe.recipeTitle,ingredients: recipe.ingredients, instructions: recipe.instructions});
this.setState({recipes: curRecipes});
},
render: function(){
return(
<div id="recipeBox">
<RecipeList recipes={this.state.recipes} />
<div className="recipeButtons">
<button id="addRecipeButton" className="btn-style" onClick={this.openForm}>Add Recipe</button>
</div>
{this.state.showForm ? this.refs.dialogWithCallBacks.show() : null}
<SkyLight
dialogStyles={formDialog}
ref="dialogWithCallBacks"
title="Add Recipe">
<RecipeForm onRecipeSubmit={this.handleRecipeSubmit} skylightRef={this.refs.dialogWithCallBacks} />
</SkyLight>
</div>
);
}
});
Recipe Form:
var RecipeForm = React.createClass({
getInitialState: function(){
return {hideDialog: false};
},
getFormData: function(){
var ingredients= document.getElementsByClassName("ingredient"),
recipeName = document.getElementsByName('recipeName')[0].value,
instructions = document.querySelector('textarea').value,
data = [];
ingredients = [].slice.call(ingredients).map(function(ingredient,index){
return {
"quantity": ingredient.childNodes[0].value,
"ingredient": ingredient.childNodes[1].value,
"unit": ingredient.childNodes[2].value
};
});
// Combine results into output array
data.push(recipeName);
data.push(ingredients);
data.push(instructions);
return data;
},
submitRecipe: function(event){
event.preventDefault();
var data = this.getFormData();
// Hide the SkyLight modal container
this.setState({hideDialog: true});
// Submit form
this.props.onRecipeSubmit({recipeTitle: data[0], ingredients: data[1], instructions: data[2]});
},
render: function(){
return(
<form onSubmit={this.submitRecipe}>
<section className="recipe-main">
<h2 style={{'border-bottom': 'none'}}>Recipe Name</h2>
<RecipeFormName />
<h2 style={{'border-bottom': 'none'}}>Ingredients</h2>
<RecipeFormIngredients />
</section>
<RecipeFormInstructions />
<input type="submit" value="Add Recipe" />
{this.state.hideDialog ? this.props.skylightRef.hide() : null}
</form>
)
}
});
You should move the code in componentWillMount to getInitialState.
getInitialState: function(){
return {recipes: this.props.recipes};
},
Needed to change the RecipeList component to
<RecipeList recipes={this.state.recipes} onChange={this.handleChange}/>
and then handle the deletion change from the RecipeBox instead of directly in RecipeList. Have to use this.props.map... to display new recipes and also delete visible ones.
var RecipeList = React.createClass({
getInitialState: function(){
return {recipes: this.props.recipes};
},
deleteRecipe: function(recipe){
var curRecipes = this.props.recipes.slice('');
curRecipes.splice(recipe.recipeKey,1);
this.props.onChange({recipes: curRecipes});
},
render: function(){
var recipeNodes = this.props.recipes.map(function(recipe,index){
return <Recipe onDelete={this.deleteRecipe} recipeKey={index} key={index} recipeName={recipe.recipeName} ingredients={recipe.ingredients} instructions={recipe.instructions} />
},this);
return(
<div>
{recipeNodes}
</div>
);
}
});
I'm learning react. In the below Alert component I'm updating a simple alert message and setting the opacity to 1 and then fading it out with a CSS transition. This is in a simple to do app to get ramped up on react.
The issue I'm having is when I update the alert text this.props.message the previous message text shows for a fraction of a second after opacity is set to 1, before the new message replaces it. It is very quick but it seems like there is a race condition as the DOM or React renders the text change slower than the style change. Let me know if there is a way I can omit the hiccup and set opacity after the text message has changed. Thanks.
var TodoApp = React.createClass({
getInitialState: function() {
return {
items: [],
alert: { message: '', success: null }
};
}
genericMethod: function() {
...
this.setState({ alert: { message: message, success: true } });
...
},
...
render: function() {
return (
<div>
<Alert message={this.state.alert.message} success={this.state.alert.success} />
<ItemInput onItemSubmit={this.addItem} />
<ItemList items={this.state.items} deleteItem={this.deleteItem} toggleComplete={this.toggleComplete} />
</div>
);
}
});
...
var Alert = React.createClass({
delayTime: 1000,
getInitialState: function() {
return {
visible: false
};
},
componentWillReceiveProps: function() {
this.setState({ visible: true }, function() {
this.setTimer();
});
},
setTimer: function() {
setTimeout(function(argument) {
this.setState({ visible: false });
}.bind(this), this.delayTime);
},
render: function() {
var style = {
opacity: (this.state.visible ? 1 : 0),
transition: 'opacity '+(this.state.visible ? 0 : this.delayTime)+'ms'
};
var alertClass = 'alert'+(this.props.success ? ' success' : ' error');
return (
<div className={alertClass} style={style}>
{this.props.message}
</div>
);
}
});
The solution was to remove the alert state from the top level Todo class. So { items: [ ], alert: { message: '', success: null } } became just { items: [ ] } and then call a method on the child Alert component from my Todo component with <Alert ref="alert" /> and this.refs['alert'].triggerAlert(message, true); (as seen in this SO answer React.js - access to component methods) instead of watching componentWillReceiveProps in the Alert component. This made it so I was not prematurely rendering the Alert component before the new message was set every time the Todo items were added or updated.
I have a select all component rendering in my table header, I would like it to check all of the checkboxes on the page when checked, if one of the checkboxes is unchecked I would like the select all to unselect too:
var SelectAll = React.createClass({
handler: function(e) {
e.target.value;
e.preventDefault();
channel.publish({
channel: "contact",
topic: "selectAll",
data: {
contacts: this.props.data
}
});
},
render: function() {
console.log(this.props.data.children);
var id = this.props.contacts;
var isSelected = this.props.data.isSelected;
return (
<div className="contact-selector">
<input type="checkbox" checked={isSelected}
onChange={this.handler} />
</div>
);
},
});
My checkboxes are rendered in each table row:
var ContactSelector = React.createClass({
getInitialState: function() {
return {
selectedContacts:[]
};
},
handleChange: function(e) {
var id = e.target.attributes['data-ref'].value;
if (e.target.checked === true){
contactChannel.publish({
channel: "contact",
topic: "selectedContact",
data: {
id: id
}});
} else{
contactChannel.publish({
channel: "contact",
topic: "deselectedContact",
data: {
id: id
}});
}
},
render: function() {
var id = this.props.data.id;
var isSelected = this.props.data.IsSelected;
return (
<div className="contact-selector">
<input type="checkbox"
checked={isSelected} data-ref={id}
onClick={this.handleChange} />
</div>
);
}
});
Can anyone please tell me how to setup the check all? Using state or refs etc
In my React.js to-do app, I'm trying to enable the return key to submit an item from my TextInput component to my ToDoList component. Right now the TextInput.inputSubmit method just console.logs the input value, but I'm wondering if I can have it trigger a prop (enter={that.addToDo}) inside of ToDoList. Or is there a better way?
JSFiddle
Edit: improved JSFiddle (courtesy of knowbody)
/** #jsx React.DOM */
var todos = [{text: "walk dog"}, {text: "feed fish"}, {text: "world domination"}, {text: "integrate return key"}];
var TextInput = React.createClass({
getInitialState: function() {
return {text: ''};
},
inputSubmit: function() {
//I think I want to trigger ToDoList's addToDo method from here?
console.log(this.refs.inputEl.getDOMNode().value);
this.setState({text: ''});
},
handleChange: function(evt) {
this.setState({text: evt.target.value});
},
handleKeyDown: function(evt) {
if (evt.keyCode === 13 ) {
return this.inputSubmit();
}
},
render: function() {
return (
<input value={this.state.text} ref="inputEl" onChange={this.handleChange} onKeyDown={this.handleKeyDown}/>
)
}
});
var SubmitButton = React.createClass({
render: function(){
return (
<button onClick={this.props.click}> Add </button>
)
}
});
var ToDo = React.createClass({
render: function(){
return (
<div>
<button onClick={this.props.click}>X</button>
<span> - {this.props.text}</span>
</div>
)
}
});
var ToDoList = React.createClass({
getInitialState: function (){
return {
todos: this.props.todos.splice(0)
}
},
deleteToDo: function(todo){
this.state.todos.splice(this.state.todos.indexOf(todo), 1);
this.setState({todos: this.state.todos});
},
addToDo: function(){
this.state.todos.push({text: this.refs.textIn.refs.inputEl.getDOMNode().value});
this.setState({
todos: this.state.todos
});
this.refs.textIn.setState({text: ''});
},
render: function(){
var that = this;
return (
<div>
{this.state.todos.map(function(todo) {
return (
<ToDo text={todo.text} click={that.deleteToDo.bind(null, todo)} />
)
})}
<br/>
<TextInput ref="textIn" enter={that.addToDo} />
<SubmitButton click={that.addToDo} />
</div>
)
}
});
React.renderComponent(<ToDoList todos={todos} />, document.body);
Your code is a bit messy but a quick fix will be to add:
this.props.enter(this.refs.inputEl.getDOMNode().value);
where your console.log() is. I will edit my answer with the full explanation once I'm on my laptop