Child Component is not able to trigger state change - javascript

Simple React State Example. The parent component is App which starts out just showing a button, when the button is clicked it should render AllRecipes (this works and I am able to manage state of AllRecipes). Inside AllRecipes is a button which needs to trigger a state change to then further render ingredients (this button does nothing when clicked, it needs to toggle the state of ingredients). I think this is a really good small example of how to manage state but I am missing something..
var App = React.createClass({
getInitialState: function(){
return {showIngredients: false, showRecipes: false};
},
toggleRecipes: function(){
this.setState({showRecipes: !this.state.showRecipes})
},
toggleIngredients: function(){
this.setState({showRecipes: !this.state.showRecipes})
},
render: function() {
var recipes = this.state.showRecipes ? <Recipes/> : null;
return (
<div>
<h1> Recipe App </h1>
<button onClick={this.toggleRecipes}> Show Recipes </button>
{recipes}
</div>
);
}
});
var Recipes = React.createClass({
render: function() {
var ingredients = this.props.showIngredients ? <Ingredients/> : null;
return (
<div>
<h1> list of recipes </h1>
<ul>
<li> Chicken Soup </li>
<li> Chicken Wings </li>
<button onClick={this.props.toggleIngredients}> Show Recipes </button>
{ingredients}
</ul>
</div>
);
}
});
var Ingredients = React.createClass({
render: function() {
return (
<div>
<h1> List of Ingredients </h1>
<ul>
<li> Salt </li>
<li> Pepper </li>
</ul>
</div>
);
}
});
React.render(<App/>, document.body);

It looks like you aren't passing toggleIngredients to Recipes. Try changing
var recipes = this.state.showRecipes ? <Recipes/> : null;
to
var recipes = this.state.showRecipes ? <Recipes toggleIngredients={this.toggleIngredients} /> : null;

Related

Error passing function onClick in react

I have two files. A list Component and a Single Item Component. In my app, the user can select multiples items. Then I create an state element in "list" "items" and my idea is that when the user make click on the item button, the list element notify to List Component and save the item in Items array from "list".
I have the next code
List.jsx:
registrarItems(data,item){
console.log(data,"aqui 1 con",item);
let items = this.state.itemsAgregados.slice();
if(!items.indexOf(data.id_producto)){
console.log("no se encuentra");
items.push(id);
this.setState({
'itemsAgregados':items
});
}else{
console.log("ya existe");
item.removerSeleccion();
}
console.log("registrando items",id);
}
render() {
return (
<div className="content-app">
<Navbar data={this.menu}/>
<div className="container lista-productos">
{
this.state.productos.map((producto, index) => {
return (
<Item data={producto}
registro = {this.registrarItems}
key={producto.id_producto}/>
);
})
}
</div>
</div>
);
}
And Item.jsx:
render() {
let props = this.props;
let img = JSON.parse(props.data.imagen);
let imgPath = Rutas.apiStatic + 'img/productos/' + props.data.id_producto + '/' + img.sm;
props.data.items = this;
return (
<div className="row item-listado">
<div className="col-xs-3">
<img src={imgPath} className="img-circle img-item"/>
</div>
<div className="col-xs-7">
<Link to={Rutas.producto + props.data.identificador}>
<h3 className="titulo">{props.data.titulo}</h3>
<span className="price">$ {props.data.precio}</span>
</Link>
</div>
<div className="col-xs-2 text-right">
<ul className="list-unstyled list-acciones">
<li>
<a href="#" onClick={()=>props.registro(props.data,this)} className={this.state.classAdd}>
<i className="fa fa-plus"></i>
</a>
</li>
</ul>
</div>
</div>
)
}
As you can see, I pass the "registrarItems" method as a param to Item and there, i add this as onClick event in the tag from item. But I need pass the "data" and the own "item" element to the onclick function. The first, for save the element clicked in the Items array, or remove it (if it already exists) because the button may have a toggle function. But in my "console.log" both params passed on the onClick method with the arrow function shows as "undefined".
I saw some examples and i don't get my error. can anybody helpme? thanks.
The final solve for this was simple. I resolved it with something similar as Free-soul said on his comment.
First, I passed the List Component as a param to item. Below my code in List's render method:
{
this.state.productos.map((producto, index) => {
this.items[producto.id_producto] = producto;
return (
<Item data={producto}
parent = {this}
key={producto.id_producto}/>
);
})
}
Then I get the parent param in componentDidMount method and later I call the validarItem function directly from the List method and I pass the params that i need.
Here my Item code:
onClickPlus(id,data) {
//{Rutas.listas + 'productos/' + props.data.id_producto //Url para pasar uno solo
this.setState({
classAdd:'selected'
})
if(!this.state.parent.validarItem(this.state.data)){
this.removerSeleccion()
}
if(this.state.seleccionMultiple){
}
}
removerSeleccion(){
this.setState({classAdd:'normal'})
}
componentDidMount(){
this.setState({
parent: this.props.parent,
data : this.props.data
})
}
render() {
return (
// more code
<a href="#" onClick={() => this.onClickPlus(parent)} className={this.state.classAdd}>
<i className="fa fa-plus"></i>
</a>
//more render code...
)
}
I don't know if this is the best practice, but works for me.

ReactJs: target.key undefined after method is reached from li rendered by map function

I'm currently trying to coding a react app that would do the following:
- Create a list of questions from an array using a map function.
- Making each list element clickable using a onClick prop
- The linked onClick method changes the state in another file with my 'qsChange' prop.
I had a hard time making my list clickable and finally managed following this question: React: trying to add an onClick to a li tag but the click handler function is undefined
However, now I cannot make it so that my variable 'choice' returns a defined value. I would want var choice to be equal to "A ?", "B ?" or "C ?" depending on which I click.
Here's my code:
var questions = ["A ?", "B ?", "C ?"];
var Questions = React.createClass({
handleClick: function() {
var visibility;
if(this.props.visibility) {
document.getElementById('second').style.display = 'none';
visibility = false;
this.props.onChange(visibility);
} else {
document.getElementById('second').style.display = 'block';
visibility = true;
this.props.onChange(visibility);
}
},
/* Here is where my problem lies */
onItemClick: function(e){
var choice = e.target.key;
this.props.qsChange(choice);
alert(choice);
},
render: function() {
return (
<div className="bigqs">
<div id="first" className="small" style={firstStyle}>
<h1>Question :</h1>
<button style={btnStyle} onClick={this.handleClick}>
<img id="arrow" src="../../img/arrow.png" />
</button>
<h3 id="selectedQuestion">{this.props.selected}</h3>
</div>
<div id="second" className="small" style={{display: 'none'}}>
<h4>
<ul>
{questions.map(function(qs, i) {return <li key={qs[i]} onClick={this.onItemClick}>{qs}</li>;}, this)}
</ul>
</h4>
</div>
</div>
);
}
});
I am still a newbie, so please be indulgent ;-)
I hope I was clear enough.
Ps: I have also tried this guide but it didn't work for me: http://derpturkey.com/react-pass-value-with-onclick/
Instead of grabbing the question from target, you can pass question through to your handler. Also, since inside map qs is a string, qs[i] will be getting the character in the string from that index. You just need to make sure your key is unique.
onItemClick: function(choice) {
this.props.qsChange(choice)
alert(choice)
},
render() {
return (
<div>
...
{questions.map(qs =>
<li key={qs} onClick={() => this.onItemClick(qs)}>{qs}</li>
)}
...
</div>
)
}
In fact, your intermediate function isn't doing much, you can just call your props function inside render:
render() {
return (
<div>
...
{questions.map(qs =>
<li key={qs} onClick={() => this.props.qsChange(qs)}>{qs}</li>
)}
...
</div>
)
}

ReactJS Different Classes

Iam new to ReactJS and I am working on a filterable gallery but now Iam looking at an example of thinking in ReactJS and I saw that they are building different classes for each component. I didn't do that but now Iam trying to do this, First my code looked like this: `
var SearchBar = React.createClass({
getInitialState() {
return { text:'', array: this.props.array};
},
handleChange(event) {
var array = this.filterList(event.target.value);
this.setState({ text: event.target.value, array: array });
return this.state.text;
},
render() {
var arrayComponents = this.state.array.map(function(photo) {
return <li className="photo photo-name">{photo.name} <img className="photo" src={photo.link}/></li>;
});
return <div>
<h1>Hello, {this.props.name}</h1>
<p>{this.state.text}</p>
<input type="text" onChange={this.handleChange} />
<ul>
{arrayComponents}
</ul>
</div>;
},
filterList (filterText) {
var updatedList = this.props.array,
filterTextLength = filterText.length;
return updatedList.filter(function(item){
var splitName = item.name.toLowerCase().slice(0, filterTextLength);
var lowerCaseFilterText = filterText.toLowerCase();
return splitName === lowerCaseFilterText;
});
}
});
Now I want to create an other Class of ImageList which has to include the var arrayComponents but if i do this:
var ImageList = React.createClass({
render() {
var arrayComponents = this.props.array.map(function(photo) {
return <li className="photo photo-name">{photo.name} <img className="photo" src={photo.link}/></li>;
});
<ul>
{arrayComponents}
</ul>
}
})
and than in the render function add <ImageList array={array}/> instead of <ul>{arrayComponent}</ul> it throws me the error that Cannot read property 'map' of undefined How do I pass the state of array into that ImageList class.
Here is a codepen: LINK
I've made the following changes in your code: http://codepen.io/PiotrBerebecki/pen/zKRAGZ
1) Pass the state of array into that ImageList class
<ImageList array={this.state.array} />
2) Add a return statement in the render method of ImageList
// Add return
return (
<ul>
{arrayComponents}
</ul>
);
3) Add key attribute to the li tag when using map method:
var arrayComponents = this.props.array.map(function(photo, index) {
// -------------------------------
// Add index to the li tag
// ----------------vvvvvvvvvvv
return <li key={index} className="photo photo-name">{photo.name} <img className="photo" src={photo.link}/></li>;
});
React Docs: https://facebook.github.io/react/docs/multiple-components.html#dynamic-children
You need to pass the current state of the array. So your component declaration should look like this:
<ImageList array={this.state.array}/>
I glance three problems here.
First,
var ImageList = React.createClass({
render() {
var arrayComponents = this.props.array.map(function(photo) {
return <li className="photo photo-name">{photo.name} <img className="photo" src={photo.link}/></li>;
});
<ul>
{arrayComponents}
</ul>
}
})
you should say:
return (<ul>
{arrayComponents}
</ul>)
Second, when you use dynamically generated codes, you should add a key prop to it:
var arrayComponents = this.props.array.map(function(photo) {
return <li key={SOME_KEY} className="photo photo-name">{photo.name} <img className="photo" src={photo.link}/></li>;
});
Third, in your codepen when you say :
<ImageList array={array}/>
you are referencing the global variable array (you declare it on the top of your code), do you mean:
<ImageList array={this.state.array}/>

How to define in which category display React component?

I have React component <Banner /> which is just an image (clickable and redirects to one of the categories), shown on the Home Page and belongs to 1 category eg. Ice-Cream (I have sidebar-menu with categories).
How can i check in which category to display this banner?
var Banner = React.createClass({
render: function() {
var toCategory = "/favourites";
var imageName = "Magnum Banner";
return (
<section className="banner" onClick={ this.props.onClick }>
<a href={toCategory}>
<img src={ window.assets['banner.png'] } width="1030" alt={imageName} ref="Banner" />
</a>
</section>
);
}
});
I was thinking about this way (this.state.ShowBanner) ? <Banner /> : null,
setting an Initial state to false in CategoryList component and then if window.location(?) equal to path in Banner component - setState to true
Pls help me with this issue. Thanks

checking for first key in map with reactjs

I've got the following component in ReactJS
var MainMenu = React.createClass({
render: function() {
console.log(this.props.groupsData);
var categories = this.props.groupsData.objects.map(function(obj){
return (<li>{obj.display_name}</li>);
});
return (<div className="MainMenu">
<ul className="nav nav-pills">{categories}</ul>
</div>);
}
});
Now, I wish to add className='active' to the <li> element if its the first in the map. How would I achieve this?
Use JS in your expression
var MainMenu = React.createClass({
render: function() {
console.log(this.props.groupsData);
var categories = this.props.groupsData.objects.map(function(obj, index) {
// You can have a JavaScript expression in your expression
return ( <li className={index == 0 ? 'active' : ''}> <a href="#">{
obj.display_name
}</a></li> );
});
return ( <div className="MainMenu" >
<ul className="nav nav-pills" >{
categories
}</ul>
</div> );
}
});

Categories