How to implement a toggle effect with react js? - javascript

I have been searching for a long time in google. But I haven't found anything. I am looking for a way to implement a toggle effect with reactjs.
Please have a look at my code below.
var Demo = React.createClass({
getInitialState:function(){
return {show:false};
},
onClick:function(){
this.setState({show:true});
},
render: function(){
return (
<div>
<div className="demo" onClick={this.onClick}>
Demo
{this.state.show?<Demosub />:null}
</div>
<div className="demo" onClick={this.onClick}>
Demo
{this.state.show?<Demosub />:null}
</div>
<div className="demo" onClick={this.onClick}>
Demo
{this.state.show?<Demosub />:null}
</div>
</div>
);
}
});
var Demosub = React.createClass({
render: function(){
return (
<div>Demo sub</div>
);
}
});

I would encapsulate each item in its own component like that:
var Demo = React.createClass({
render: function() {
return (
<div>
<DemoItem />
<DemoItem />
<DemoItem />
</div>
);
}
});
var DemoItem = React.createClass({
getInitialState: function() {
return { show: false };
},
toggleShow: function() {
this.setState({ show: !this.state.show });
},
render: function() {
return (
<div className="demo" onClick={this.toggleShow}>
Demo
{this.state.show ? <DemoSub /> : null}
</div>
);
}
});
var DemoSub = React.createClass({
render: function() {
return <div>Demo sub</div>
}
});

import React, { Component } from 'react';
class Person extends Component {
state={
showPerson:false,
}
toggleName=()=>{
const doesShow = this.state.showPerson;
this.setState({
showPerson:!doesShow
})
}
render() {
let personList = null;
if(this.state.showPerson) {
personList = (
<div> Show Content </div>
)
}
return (
<div>
<button onClick={this.toggleName}>Switch</button>
{personList}
</div>
);
}
}
export default Person;

Related

React - send function props to Children

I saw some questions speaking about similar issues but somehow I still do not manage to solve my issue so here I am asking for your kind help. I am pretty new to React and would like to send a function from a Parent to a child and then use it from the Child but somehow when I want to use it it says
Uncaught TypeError: Cannot read property 'props' of undefined"
Edited Code after first answers were helping:
var Menu = React.createClass({
links : [
{key : 1, name : "help", click : this.props.changePageHelp}
],
render : function() {
var menuItem = this.links.map(function(link){
return (
<li key={link.key} className="menu-help menu-link" onClick={link.click}>{link.name}</li>
)
});
return (
<ul>
{menuItem}
</ul>
)
}
});
var Admin = React.createClass ({
_changePageHelp : function() {
console.log('help');
},
render : function () {
return (
<div>
<div id="menu-admin"><Menu changePageHelp={this._changePageHelp.bind(this)} /></div>
</div>
)
}
});
ReactDOM.render(<Admin />, document.getElementById('admin'));
Pass a value from Menu function and recieve it in the changePageHelp function and it works.
var Menu = React.createClass({
render : function() {
return (
<div>
{this.props.changePageHelp('Hello')}
</div>
)
}
});
var Admin = React.createClass ({
_changePageHelp : function(help) {
return help;
},
render : function () {
return (
<div>
<div id="menu-admin"><Menu changePageHelp={this._changePageHelp.bind(this)} /></div>
</div>
)
}
});
ReactDOM.render(<Admin />, document.getElementById('admin'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="admin"></div>
For performance reasons, you should avoid using bind or arrow functions in JSX props. This is because a copy of the event handling function is created for every instance generated by the map() function. This is explained here: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md
To avoid this you can pull the repeated section into its own component. Here is a demo: http://codepen.io/PiotrBerebecki/pen/EgvjmZ The console.log() call in your parent component receives now the name of the link. You could use it for example in React Router.
var Admin = React.createClass ({
_changePageHelp : function(name) {
console.log(name);
},
render : function () {
return (
<div>
<div id="menu-admin">
<Menu changePageHelp={this._changePageHelp} />
</div>
</div>
);
}
});
var Menu = React.createClass({
getDefaultProps: function() {
return {
links: [
{key: 1, name: 'help'},
{key: 2, name: 'about'},
{key: 3, name: 'contact'}
]
};
},
render: function() {
var menuItem = this.props.links.map((link) => {
return (
<MenuItem key={link.key}
name={link.name}
changePageHelp={this.props.changePageHelp}
className="menu-help menu-link" />
);
});
return (
<ul>
{menuItem}
</ul>
);
}
});
var MenuItem = React.createClass ({
handleClick: function() {
this.props.changePageHelp(this.props.name);
},
render : function () {
return (
<li onClick={this.handleClick}>
Click me to console log in Admin component <b>{this.props.name}</b>
</li>
);
}
});
ReactDOM.render(<Admin />, document.getElementById('admin'));

Add className to li item react

I am trying to update the active class when a user clicks on a button. Each time this button is clicked on the next item should have the class name active added and removed from the previous one. Using jQuery I could use the addClass and removeClass methods. Something along these lines would get me started.
$("button").click(function() {
$("ul li").removeClass("active");
var length = $(this).prevAll().length;
$("ul li:eq(" + length + ")").addClass("active");
});
I am not sure how I should go about implementing this using react.
I have set up a JSFIDDLE here.
var App = React.createClass({
getInitialState: function(){
return {
active: 0
}
},
incrementIndex: function() {
this.setState({active : this.state.active + 1});
},
render: function () {
return(
<div>
<ArtistList active={this.state.active} />
<Button increment={this.incrementIndex} />
</div>
)
}
});
var ArtistList = React.createClass({
render: function() {
return(
<ul>
<li className="active">Michael Jackson</li>
<li>Bob Dylan</li>
<li>Kendrick Lamar</li>
</ul>
)
}
});
var Button = React.createClass({
render: function() {
return(
<button onClick={this.props.increment}>next</button>
)
}
})
You can generate the list elements dynamically by iterating over an array containing the list content. Then you can simply compare the passed prop with the current iteration:
var data = ['Michael Jackson', 'Bob Dylan', 'Kendrick Lama'];
var listItems = data.map((value, index) => (
<li
key={value}
className={this.props.active === index ? 'active' : ''}>
{value}
</li>
));
return <ul>{listItems}</ul>;
I would pass in artists as a prop to ArtistList, and then in AristList use a map function to generate a list of li's and use a ternary to figure out if it should be active or not.
have the pen here http://codepen.io/finalfreq/pen/bwoddN
var App = React.createClass({
getInitialState: function(){
return {
active: 0
}
},
incrementIndex: function() {
this.setState({active : this.state.active + 1});
},
render: function () {
const artistList = ['Michael Jackson', 'Bob Dylan', 'Kendrick Lamar']
return(
<div>
<ArtistList artists={artistList} active={this.state.active} />
<Button increment={this.incrementIndex} />
</div>
)
}
});
var ArtistList = React.createClass({
renderArtists: function() {
var self = this;
return this.props.artists.map(function(artist, index) {
var classes = index === self.props.active ? 'active' : '';
return <li key={index + artist} className={classes} > { artist } </li>
});
},
render: function() {
let artists = this.renderArtists();
return(
<ul>
{artists}
</ul>
)
}
});
var Button = React.createClass({
render: function() {
return(
<button onClick={this.props.increment}>next</button>
)
}
})
ReactDOM.render(<App />, document.getElementById('app'));

React: Warning: setState(...): Cannot update during an existing state transition

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>
);
}
});

ReactJS - Target random element

I have some problems understanding how you interact with element in React after rendering. I currently got this fiddle: https://jsfiddle.net/y7dh3vh5/
var items = ["Afghanistan","Albania","Algeria","Andorra","Angola"....
var RepeatModule = React.createClass({
getDefaultProps: function() {
return { items: [] }
},
render: function() {
var listItems = this.props.items.map(function(item, i) {
return (
<div className="item" key={i}>
<p>{item}</p>
</div>
);
});
return (
<div>
{listItems}
</div>
);
}});ReactDOM.render(<RepeatModule items={items} />, document.getElementById('itemList'));
And I'm looking for a way to highlight a random country when I press the "Highlight random country"-button. Is there an easy way to implement this?
Thanks in advance.
Add state to save highlightedIndex:
getInitialState () {
return {
highlightedIndex: -1
}
},
Add method for button
setNewTarget: function() {
var l = this.props.items.length - 1;
this.setState({
highlightedIndex: this.randomInteger(0, l)
})
},
Put button into return render
return (
<div>
<button onClick={this.setNewTarget}>
Highlight random country
</button>
{listItems}
</div>
);
Live example: https://jsfiddle.net/ufmagg4o/
Just keep your index element in state and compare it in map method. I prefere es6 so i hope you will understand
class Example extends React.Component {
constructor(){
this.state = {
items: ['hello', 'world', 'random', 'highlight'],
index: null
}
this.click = this.click.bind(this);
}
click(){
const items = this.state.items;
const index = Math.floor(Math.random()*items.length);
this.setState({index: index})
}
render(){
const list = this.state.items.map((item,index)=>{
return <p key={index} className={this.state.index === index ? 'item': null}>
{item}
</p>
})
return <div>
<button onClick={this.click}>Click me</button>
{list}
</div>
}
}
React.render(<Example />, document.getElementById('container'));
fiddle example
Thanks!
The key is to keep that button inside React. Don't try to manipulate React components from outside React.
<script src="https://facebook.github.io/react/js/jsfiddle-integration-babel.js"></script>
<div id="itemList">
</div>
.highlighted {
color: white;
background-color: tomato;
}
var items = [
"Afghanistan",
"Albania",
"Algeria",
"Andorra",
"Angola"
]; // etc...
var RepeatModule = React.createClass({
getDefaultProps: function() {
return { items: [] }
},
getInitialState() {
return {highlighted: null};
},
onClickButton() {
this.setState({
highlighted: (Math.random() * this.props.items.length)|0
});
},
render: function() {
var _this = this;
var listItems = this.props.items.map(function(item, i) {
return (
<div className="item" key={i}>
<p className={i == _this.state.highlighted ? 'highlighted' : null}>
{item}
</p>
</div>
);
});
return (
<div>
<button onClick={this.onClickButton}>
Highlight random country
</button>
<div>
{listItems}
</div>
</div>
);
}
});
ReactDOM.render(<RepeatModule items={items} />, document.getElementById('itemList'));
jsfiddle
ES2015 version:
const items = [
"Afghanistan",
"Albania",
"Algeria",
"Andorra",
"Angola"
]; // etc...
class RepeatModule extends React.Component {
static propTypes = {items: React.PropTypes.arrayOf(React.PropTypes.string)};
static defaultProps = {items: []};
state = { highlighted: null };
onClickButton = () => {
this.setState({
highlighted: (Math.random() * this.props.items.length)|0
});
};
render() {
const listItems = this.props.items.map((item, i) => (
<div className="item" key={i}>
<p className={i == this.state.highlighted ? 'highlighted' : null}>
{item}
</p>
</div>
));
return (
<div>
<button onClick={this.onClickButton}>
Highlight random country
</button>
<div>
{listItems}
</div>
</div>
);
}
});
ReactDOM.render(<RepeatModule items={items} />, document.getElementById('itemList'));

Cross-component communication via return key

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

Categories