Passing data to parent - recipebook React - javascript

I am working on the recipe book app and trying to implement the edit entry function.
How it works is to input recipe name (e.g Pasta), followed by ingredients (e.g egg, flour, salt). The ingredients have to be input with commas and will be shown as a list.
Pasta
-Egg
-Flour
i can see that it is somewhat working, because i can see the new entries in the input text (e.g initially was egg,flour,salt -> egg,flour,salt,water) when i tried to edit it again.
However, the extra ingredients (in the above example: water) is not showing up in the list. Do i have to figure a way to re-render the list?
updates:
I think i know where the error might be. There is some issue passing the data and setting the state.
<EditRecipe recipe={this.props.recipe} editRecipe={this.editRecipe.bind(this, this.props.recipe.id, recipe)}/>
App.js
import React, { Component } from 'react';
// import logo from './logo.svg';
import './App.css';
import uuid from 'uuid';
import Modal from 'react-modal';
import RecipeList from './components/RecipeList/RecipeList';
import AddRecipe from './components/AddRecipe/AddRecipe';
class App extends Component {
constructor(props){
super(props);
this.state = {
recipes:[]
};
}
getRecipes(){
this.setState({recipes:[
{
id: uuid.v4(),
food: "pumpkin pie",
ingredients: ["pumpkin puree", "sweetened condensed milk", "eggs", "pumpkin pie spice", "pie crust"]
},
{
id: uuid.v4(),
food: "spaghetti",
ingredients: ["noodles", "tomato sauce", "meatballs"]
},
{
id: uuid.v4(),
food: "onion pie",
ingredients: ["onion", "pie crust"]
},
]});
}
componentWillMount(){
this.getRecipes();
}
handleAddRecipe(recipe){
let recipes = this.state.recipes;
recipes.push(recipe);
this.setState({recipes: recipes});
}
handleDeleteRecipe(id){
let recipes = this.state.recipes;
let index = recipes.findIndex(x => x.id === id);
recipes.splice(index,1);
this.setState({recipes: recipes});
}
handleEditRecipe(id, recipe){
let recipes = this.state.recipes;
let index = recipes.findIndex(x => x.id === id);
recipes.splice(index,1,recipe);
this.setState({recipes: recipes});
}
render() {
return (
<div className="App">
<RecipeList recipes={this.state.recipes} onDelete={this.handleDeleteRecipe.bind(this)} onEdit={this.handleEditRecipe.bind(this)}/>
<AddRecipe addRecipe={this.handleAddRecipe.bind(this)}/>
</div>
);
}
}
export default App;
RecipeList.js
import React, { Component } from 'react';
import Collapsible from 'react-collapsible';
import RecipeItem from '../RecipeItem/RecipeItem'
import './RecipeList.css';
class RecipeList extends Component{
deleteRecipe(id){
this.props.onDelete(id);
}
editRecipe(id, recipe){
this.props.onEdit(id, recipe);
}
render(){
let recipeItem;
if(this.props.recipes){
recipeItem=this.props.recipes.map(recipe => {
return(
<RecipeItem onEdit={this.editRecipe.bind(this)} onDelete={this.deleteRecipe.bind(this)} key={recipe.id} recipe={recipe} />
)
});
}
return(
<div className="recipeList box">
{recipeItem}
</div>
)
}
}
export default RecipeList;
RecipeItem.js
import React, { Component } from 'react';
import Collapsible from 'react-collapsible';
import EditRecipe from '../EditRecipe/EditRecipe';
class RecipeItem extends Component{
deleteRecipe(id){
this.props.onDelete(id);
}
editRecipe(id, recipe){
this.props.onEdit(id, recipe);
}
render(){
let recipe=this.props.recipe
let foodName=recipe.food;
let ingredientItem;
if(recipe.ingredients){
ingredientItem=recipe.ingredients.map(ingredient=>{
return(
<a className="panel-block">
{ingredient}
</a>
)
})
}
return(
<ul>
<li className="Recipe">
<Collapsible trigger={foodName} transitionTime="200" easing="ease-in-out">
<nav className="panel">
<p className="panel-heading">
Ingredients
</p>
{ingredientItem}
<div className="panel-block">
<button className="button is-warning is-outlined" onClick={this.deleteRecipe.bind(this, this.props.recipe.id)}>
Delete
</button>
<EditRecipe recipe={this.props.recipe} editRecipe={this.editRecipe.bind(this, this.props.recipe.id, recipe)}/>
</div>
</nav>
</Collapsible>
</li>
</ul>
);
}
}
export default RecipeItem;
EditRecipe.js
import React, { Component } from 'react';
import RecipeForm from '../RecipeForm/RecipeForm';
// import './EditRecipe.css';
import Modal from 'react-modal';
import uuid from 'uuid';
// import Modal from 'boron/DropModal';
// import './RecipeList.css';
class RecipeEdit extends Component{
constructor(props){
super(props);
this.state = {
revisedRecipe:{
id: this.props.recipe.id,
food: this.props.recipe.food,
ingredients: this.props.recipe.ingredients
},
modalIsOpen: false,
speed: 100
};
this.openModal = this.openModal.bind(this);
this.closeModal = this.closeModal.bind(this);
}
openModal(){
this.setState({modalIsOpen: true});
}
closeModal(){
this.setState({modalIsOpen: false});
}
handleSubmit(e){
const revised = this.state.revisedRecipe;
this.props.editRecipe(revised);
e.preventDefault();
}
handleNameChange(e){
this.setState({revisedRecipe:{
food: e.target.value
}
});
}
handleIndChange(e){
this.setState({revisedRecipe:{
ingredients: e.target.value
}
});
}
render(){
const speed = this.state.speed;
let recipe=this.props.recipe;
let foodName=this.state.revisedRecipe.food;
let ingredients=recipe.ingredients;
return(
<div>
<button className="button is-primary" onClick={this.openModal}>Edit Recipe</button>
<Modal
isOpen={this.state.modalIsOpen}
onAfterOpen={this.afterOpenModal}
onRequestClose={this.closeModal}
closeTimeoutMS={speed}
contentLabel="Example Modal"
>
<div className="field">
<h2 className="title is-2">Edit Recipe</h2>
<form>
<label className="label">Recipe</label>
<div className="control">
<input className="input" type="text" placeholder="Recipe Name" ref="recipeName" value={this.state.revisedRecipe.food} onChange={this.handleNameChange.bind(this)}/>
</div>
<div className="field">
<label className="label">Ingredients</label>
<div className="control has-icons-left has-icons-right">
<input className="input" type="text" placeholder="Enter ingredients. (if more than 1 ingredient, separate them with commas)" ref="ingredients" value={this.state.revisedRecipe.ingredients} onChange={this.handleIndChange.bind(this)}/>
<span className="icon is-small is-left">
<i className="fa fa-flask"></i>
</span>
</div>
</div>
<div className="field is-grouped">
<div className="control">
<button className="button is-primary" onClick={this.closeModal}>Edit Recipe</button>
</div>
<div className="control">
<button className="button" onClick={this.closeModal}>Cancel</button>
</div>
</div>
</form>
</div>
</Modal>
</div>
);
}
}
export default RecipeEdit;

I believe you're actually getting an error when trying to re-render after updating a list. The ingredients property in the recipes are an array (as shown in getRecipes()) but you're setting the new state of ingredients (in EditRecipe) as a string: "egg,flour,salt,water" and then trying to render the ingredients as an array: ingredients.map().
When you render an input field with an array <input value={["egg", "flour"]} /> it does show the values separated by comma, but the event.target.value in onChange is actually a string.
In EditRecipe's, handleIndChange could be fixed with:
this.setState({revisedRecipe: {ingredients: e.target.value.split(',')}});
This does have another problem, though in that you are overriding the revisedRecipe completely. So all of the setState calls should be something like:
const recipe = this.state.revisedRecipe;
recipe.ingredients = e.target.value.split(',');
this.setState({revisedRecipe: recipe);

Related

Some problems with react.js

I'm recently working on react.js, and now I have two problems:
The file structure is like
>public
>src
>components
>img
x.png
Item.js
App.js
index.js
Item.js:
import React from 'react';
class Item extends React.Component{
constructor(props){
super(props);
}
render(){
return(
<li className="todo-app__item">
<div className="todo-app__checkbox">
<input type="checkbox" id={this.props.num}
checked={this.props.completed} onClick = {this.props.onClick}/>
<label htmlFor={this.props.num}></label>
</div>
<h1 className="todo-app__item-detail">{this.props.text}</h1>
<img src='./components/img/x.png' className="todo-app__item-x"/>
</li>
);
}
}
export default Item;
App.js
import './App.css';
import React from 'react';
import Item from './components/Item.js';
class Main extends React.Component{
constructor(props){
super(props);
this.state={tasks: []}
}
handleKeyDown = (e)=>{
if (e.key === 'Enter') {
this.setState (prevState => ({
tasks: [...prevState.tasks, {content: e.target.value, completed: false}]
}));
}
}
handleClick = (e) =>{
this.setState (prevState => {
let newTasks = prevState.tasks.slice();
newTasks[e].completed = !prevState.tasks[e].completed;
return{tasks: newTasks};
})
}
/*displayAll = () =>{
}
displayActive = () =>{
}
displayCompleted = () =>{
}
deleteCompleted = () =>{
}*/
render(){
return(
<section className="todo-app__main">
<input className="todo-app__input"
placeholder="What needs to be done?" onKeyDown={this.handleKeyDown} />
<ul className="todo-app__list" id="todo-list">
{this.state.tasks.map(item =>
<Item num={this.state.tasks.indexOf(item)} text={item.content} completed={item.completed}
onClick={() => this.handleClick(this.state.tasks.indexOf(item))}/>)}
</ul>
<footer className="todo-app__footer" id="todo-footer">
<div className="todo-app__total"> {this.state.tasks.filter(e=>e.completed===false).length} Left</div>
<ul className="todo-app__view-buttons">
<button>All</button>
<button>Active</button>
<button>Complete</button>
</ul>
<div className="todo-app__clean">
<button>Clear complete</button>
</div>
</footer>
</section>
);
}
}
My x.png is not showing properly. How to fix it?
The three buttons in the bottom, I hope that completed can do something like displaying the task that satisfies task[1]=true without deleting the item. How do I implement this?
You can modify the src attribute of img, the relative path is wrong now
<img src='./img/x.png' className="todo-app__item-x"/>
Use react state to render different buttons
{this.state.tasks[x].completed && <button>Complete</button>}
eg: when tasks x is completed, display button.

Edit posts in react

I am trying to edit the text inside my Products component---
import React, { Component } from 'react'
import Newitem from './Newitem'
import App from '../App';
export default class Products extends Component {
state = {
name:this.props.name,
price:this.props.price,
description:this.props.description,
rating:this.props.rating,
img:this.props.img,
newName:'',
newPrice:'',
newDescription:'',
newImg:'',
inputsDisplay:'',
index:this.props.index,
}
deleteFunc=()=>{
this.props.delete(this.state.index)
}
changeName=(e)=>{
let changeNameInput=e.target.value;
this.setState({newName:changeNameInput})
}
changePrice=(e)=>{
let priceInput=e.target.value;
this.setState({newPrice:priceInput})
}
changeDescrip=(e)=>{
}
changeImg=(e)=>{
}
editButton=()=>{
if(this.state.newName.length==0){
this.setState({
inputsDisplay:'block'
})
}
else{
this.setState({
name:this.state.newName,
price:`${this.state.newPrice} NIS`,
inputsDisplay:'block'
})
}
}
hideEditInputs=()=>{
this.setState({inputsDisplay:'none'})
}
render() {
return (
<div className='container' id='Container'>
<span className='namestyle'>{this.props.name}</span>
<span className='descriptionstyle'>{this.props.description}</span>
<img className='imgstyle' alt='Bad source' src={this.props.img}></img>
<span className='pricestyle'>{this.props.price}</span>
<span className='ratingstyle'>{this.props.rating}</span>
<button onClick={this.deleteFunc} id='deleteButton'>X</button>
<input className='editNameInput' onChange={this.changeName} placeholder='Edit name'
style={{display:this.state.inputsDisplay}}>
</input>
<button className='editButton' onClick={this.editButton}>EDIT</button>
<input onChange={this.changePrice} className='editPriceInput' placeholder='Edit price'
style={{display:this.state.inputsDisplay}}>
</input>
<input className='editDescripInput' placeholder='Edit description'
style={{display:this.state.inputsDisplay}}>
</input>
<input className='editImgInput' placeholder='Edit IMG'
style={{display:this.state.inputsDisplay}}>
</input>
<button onClick={this.hideEditInputs}className='hideEditButton' >HIDE EDIT</button>
</div>)
}
}
I have 3 components Products, Header, App, and Newitem.
The Header is for the title, the App holds my data array, Newitem is placing new 'post'(product) inside of my data array, Products get props from App.
I created 4 inputs and my goal is When I click the EDIT button the text in the input will replace the text I got from the props.
I tried with setState but it seems to bug the Newitem function(it's giving me a new product with the same info I have in the data array and not what I gave to the function)
I really clueless about what I did wrong, here are my other components Newitem---
import React, { Component } from 'react'
import Products from './Products'
export default class Newitem extends Component {
state = {
imgInput:'',
discriptionInput:'',
nameInput:'',
priceInput:'',
validNameBG:'white',
validPriceBG:'white',
validDiscriptionBG:'white',
validImgBG:'white'
}
validName=(e)=>{
let nameInput=e.target.value
console.log(nameInput)
if(nameInput.length>=4 && nameInput.length<40){
this.setState({
nameInput:nameInput,
validNameBG:'lightgreen'
})
}
else if(nameInput.length==0){
this.setState({validNameBG:'white'})
}
else{this.setState({
validNameBG:'red',
nameInput:''
})}
}
validPrice=(e)=>{
let priceInput=Number(e.target.value)
if(isNaN(priceInput)){
this.setState({validPriceBG:'red'})}
else if(!isNaN(priceInput)){
this.setState({
priceInput:priceInput,
validPriceBG:'lightgreen'
})
}
if(priceInput === 0){
this.setState({
validPriceBG:'white',
priceInput:'',
})
}
}
validDescription=(e)=>{
let discriptionInputValue=e.target.value
if(discriptionInputValue.length>=4 && discriptionInputValue.length<100){
this.setState({
validDiscriptionBG:'lightgreen',
discriptionInput:discriptionInputValue})
}
else if(discriptionInputValue.length==0){
this.setState({
validDiscriptionBG:'white'
})
}
else{this.setState({
validDiscriptionBG:'red',
discriptionInput:''
})}
}
validImg=(e)=>{
let imgInputValue=e.target.value;
if(imgInputValue.length>7){
this.setState({
validImgBG:'lightgreen',
imgInput:imgInputValue
})
}
else if(imgInputValue.length <= 7 && imgInputValue.length>0){
this.setState({
validImgBG:'red',
imgInput:''
})
}
else{
this.setState({
validImgBG:'white',
imgInput:''
})
}
}
addFunc=()=>{
if(this.state.validDiscriptionBG=='red'||this.state.validImgBG=='red'||this.state.validNameBG=='red'
||this.state.validPriceBG=='red'){
return alert('Invalid value.')
}
else if(this.state.validDiscriptionBG=='white'||this.state.validImgBG=='white'||this.state.validNameBG=='white'
||this.state.validPriceBG=='white'){
return alert('Invalid value.')
}
else{
this.props.add(
this.state.nameInput,
`${this.state.priceInput} NIS`,
this.state.discriptionInput,
this.state.imgInput,
)}
}
render() {
return (
<div>
<ul id='placing '>
<input onChange={this.validName}
id='nameInput' placeholder='Name'
style={{backgroundColor:this.state.validNameBG}}>
</input>
<input onChange={this.validPrice}
id='priceInput' placeholder='Price'
style={{backgroundColor:this.state.validPriceBG}}>
</input>
<button onClick={this.addFunc} className='plusbutton'>Add</button>
</ul>
<ul>
<input onChange={this.validDescription}
id='descriptionInput' placeholder='Description'
style={{backgroundColor:this.state.validDiscriptionBG}}
></input>
<input onChange={this.validImg}
id='imgInput' placeholder='Img Source'
style={{backgroundColor:this.state.validImgBG}}
></input>
</ul>
</div>
)
}
}
App component----
import React, { Component } from 'react'
import Header from './components/Header'
import Products from './components/Products'
import Newitem from './components/Newitem'
import '../src/App.css';;
export default class App extends Component {
state = {
products: [
{
name:'Apple iphone 11 Pro max 256gb',
price:'4749 NIS',
description:'Iphone 11 256 gb,with 4gb ram, 6.5 inch,thickness 8.1',
rating:'5/5 Stars ! ',
img:'https://img.zap.co.il/pics/3/3/2/9/52789233b.gif'
},
{
name:'Samsung Galaxy S20 Plus',
price:'3749 NIS',
description:'Android operation system 8gb of ram Thickness 7.8m,weight 186 gram',
rating:'5/5 Stars !',
img:'https://img.zap.co.il/pics/0/2/1/8/54298120b.gif'
},
{
name:'Xiamo Poco X3',
price:'1265 NIS',
description:'Android operation system 6gb of ram Thickness 9.4m,refresh rate 120Hz',
rating:'3/5 Stars ! ',
img:'https://img.zap.co.il/pics/2/8/3/8/57738382b.gif'
},
{
name:'Samsung Galaxy A71',
price:'1049 NIS',
description: 'Android operation system 6gb of ram ,48 Mega pixel Thickness 7.9m',
rating:'4/5 Stars !',
img:'https://img.zap.co.il/pics/3/1/3/3/53803313b.gif'
}]
}
addNewProduct =(n,p,d,i,r)=>{
const newProduct= {
name:n,
price:p,
description:d,
img:i,
rating:r,
}
this.setState({products:[newProduct,...this.state.products]})
}
deleteProduct=(i)=>{
let filteredProducts=this.state.products.filter((item,index)=>(index!==i))
this.setState({products:filteredProducts})
}
render() {
return (
<div className="App">
<Newitem add={this.addNewProduct}/>
<Header />
{this.state.products.map((item,index)=>{
return(
<Products
name={item.name}
price={item.price}
description={item.description}
rating={item.rating}
img={item.img}
delete={this.deleteProduct}
index={index}
/>
)
})}
</div>
)
}
}
Thank you a lot
Props should be immutable, so a component shouldn't manage is own props. You can do it the same way you are using to delete a product.
This code should be in your App component. Receive the index and update your data.
changeName=(index, e)=>{
const updatedProducts = [...this.state.products];
updatedProducts[index].name = e.target.value;
this.setState({ products: updatedProducts });
}
Then pass the function as a prop to Product component.

why does one react project use .bind(this) method but another react project does not use it? [duplicate]

This question already has answers here:
Arrow vs classic method in ES6 class
(1 answer)
Why we don't need to bind the arrow function in React?
(2 answers)
Closed 2 years ago.
I did 2 tutorials on using REACT. "to-do-app" is a django/react project while "robofriends" is a purely react project. "to-do-app" uses .bind(this) for its functions while "robofriends" does not use this method. While i understand why the .bind method is needed, i do not understand why "robofriends" was able to work without binding its functions.
to-do app (apps.js)
import React from 'react';
import './App.css';
class App extends React.Component{
constructor(){
super();
this.state={
todoList:[],
activeItem:{
id:null,
title:"",
completed:false
},
editing:false,
}
this.fetchTasks=this.fetchTasks.bind(this)
this.handleChange=this.handleChange.bind(this)
this.handleSubmit=this.handleSubmit.bind(this)
}
componentWillMount(){
this.fetchTasks()
}
fetchTasks(){
console.log('fetching')
fetch('http://127.0.0.1:8000/api/task-list/')
.then(response=>response.json())
.then(data=>
this.setState({todoList:data})
)
}
handleChange(e){
const name=e.target.name;
const value=e.target.value;
console.log('Name', name)
console.log('Value', value)
this.setState({
activeItem:{
...this.state.activeItem,
title:value
}
})
}
handleSubmit(e){
e.preventDefault()
console.log('ITEM', this.state.activeItem)
var url='http://127.0.0.1:8000/api/task-create/'
fetch(url, {
method:'POST',
headers:{
'Content-type':'application/json',
},
body:JSON.stringify(this.state.activeItem)
}).then((response) =>{
this.fetchTasks()
this.setState({
activeItem:{
id:null,
title:"",
completed:false
}
})
}).catch(function(error){
console.log('ERROR', error)
})
}
render(){
const tasks=this.state.todoList
return(
<div className="container">
<div id="task-container">
<div id="form-wrapper">
<form onSubmit={this.handleSubmit} id="form">
<div className="flex-wrapper">
<div style={{flex:6}}>
<input onChange={this.handleChange} className="form-control" id="title" type="text" name="title" placeholder="Add task" />
</div>
<div style={{flex:1}}>
<input id="submit" className="btn btn-warning" type="submit" name="Add" />
</div>
</div>
</form>
</div>
<div id="list-wrapper">
{tasks.map((task, index)=>{
return(
<div key={index} className="task-wrapper flex-wrapper">
<div style={{flex:7}}>
<span>{task.title}</span>
</div>
<div style={{flex:1}}>
<button className="btn btn-sm btn-outline-info">Edit</button>
</div>
<div style={{flex:1}}>
<button className="btn btn-sm btn-outline-dark delete">-</button>
</div>
</div>
)
})}
</div>
</div>
</div>
)
}
}
export default App;
robofriends(apps.js)
import React, {Component} from 'react';
import CardList from '../components/Cardlist';
// import {robots} from './robots';
import SearchBox from '../components/SearchBox';
import './App.css';
import Scroll from '../components/Scroll';
import ErrorBoundary from '../components/ErrorBoundary';
class App extends Component {
constructor(){
super()
this.state={
robots:[],
searchfield:''
} //a state is what changes in an app
}
componentDidMount(){
fetch('https://jsonplaceholder.typicode.com/users')
.then(response=> response.json())
.then(users => this.setState({robots:users}))
}
// remember to use => for functions you create yourself.
onSearchChange=(event) => {
this.setState({searchfield:event.target.value})
}
render(){
const {robots, searchfield}=this.state;
const filteredRobots=robots.filter(robot=>{
return robot.name.toLowerCase().includes(searchfield.toLowerCase());
})
if (!robots.length){
return <h1>Loading</h1>
}else{
return(
<div className='tc'>
<h1 className='f1'>Robofriends</h1>
<SearchBox searchChange={this.onSearchChange}/>
<Scroll>
<ErrorBoundary>
<CardList robots={filteredRobots}/>
</ErrorBoundary>
</Scroll>
</div>
);
}
}
}
export default App;
robofriends 'searchbox.js'
import React from 'react';
const SearchBox=({searchChange})=>{
return (
<div className='pa2'>
<input
className='pa3 ba b--green bg-lightest-blue'
type='search'
placeholder='search robots'
onChange={searchChange}
/>
</div>
);
}
export default SearchBox;
fetchTasks(){
if you define a function like this, then you need to .bind(this)
as this will lose the context of this of class, to maintain it we need to use .bind(this)
But if you use below one then no need to bind, it maintains the context of this, so no need of using .bind(this)
Arrow functions do not bind their own this, instead, they inherit the
one from the parent scope, which is called "lexical scoping"
fetchTasks = () => {
For more detail : DO READ
Hope the below snippet will help you get understanding :
const { useState , useEffect } = React;
class App extends React.Component {
constructor() {
super();
this.state = {
name : "Vivek"
}
this.withBound = this.withBound.bind(this);
}
withoutBound() {
console.log(this.state.name)
}
withBound() {
console.log(this.state.name)
}
withFatArrow = () => {
console.log(this.state.name)
}
render() {
return (
<div>
Check the console on click:
<br/><br/>
<button onClick={this.withoutBound}>Without Bind</button>
<br/><br/>
<button onClick={this.withBound}>With Bind</button>
<br/><br/>
<button onClick={this.withFatArrow}>Fat Arrow</button>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('react-root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react-root"></div>

Get keys of every object and insert that data into array

I want to get the keys of every frame object and insert that data into array. After it should have arrays in array. I have tried multiple ways and have not figured it out. Any suggestions?
This is the output.json that i will be working with, it could go up to 550 frame number's.
[{"frame_number": 1, "roi0": [101.78202823559488, 99.39509279584912, 49.546951219239915, 29.728170731543948], "intensity0": 80.0, "roi1": [101.78202823559488, 99.39509279584912, 49.546951219239915, 29.728170731543948], "intensity1": 157.0},
{"frame_number": 2, "roi0": [102.56623228630755, 97.95906005049548, 50.25603182631066, 30.153619095786393], "intensity0": 80.0, "roi1": [102.56623228630755, 97.95906005049548, 50.25603182631066, 30.153619095786393], "intensity1": 158.0},
{"frame_number": 3, "roi0": [103.39336535376313, 98.20468223716023, 49.58465295946593, 29.750791775679556], "intensity0": 80.0, "roi1": [103.39336535376313, 98.20468223716023, 49.58465295946593, 29.750791775679556], "intensity1": 157.0},
The following is my app.js where i get the output.json file from the api and send it through to a component, button_footer
import "bootstrap/dist/css/bootstrap.css";
import React from "react";
import Radio_Button from "./components/Radio_Button.js";
import Buttons_Footer from "./components/Buttons_Footer.js";
import LeftPane from "./components/LeftPane.js";
//import './App.css';
class App extends React.Component {
constructor(props) {
super(props);
this.state = { apiResponse: [] };
}
// Comunicate with API
callAPI() {
fetch("http://localhost:9000/IntensityAPI") //React app talks to API at this url
.then(res => res.json())
.then(res => this.setState({ apiResponse: res }));
}
componentWillMount() {
this.callAPI();
}
render() {
return (
<div className="App">
<header className="App-header">
<p></p>
<div class="row fixed-bottom no-gutters">
<div class="col-3 fixed-top fixed-bottom">
<LeftPane></LeftPane>
</div>
<div class="offset-md-3" >
<Buttons_Footer readings = {this.state.apiResponse}/>
</div>
</div>
</header>
</div>
);
}
}
export default App;
The following is the button_footer which is where I tried to handle this data and put it in a array but failed.
import $ from "jquery";
import React, { Component } from 'react';
import { MDBFormInline } from 'mdbreact';
import { Container, Row, Col } from 'reactstrap';
import Radio_Button from "./Radio_Button.js";
// Footer Component with checkbox's used to select region/region's of interest
class Buttons_Footer extends Component {
// Enables the Functionality of the "Select Multiple Region's" switch using jquerys
componentDidMount() {
$(".region").click(function(e){
if($('#customSwitches').is(':not(:checked)')){
if($('.region:checked').length > 1){ // Multiply regions unable to be selected
alert('You can not check multiple');
e.preventDefault();
}
}
});
$("#customSwitches").click(function(e){ // Multiply regions able to be selected
$(".region").prop('checked', false);
}); }
//<p>{this.props.region.roi0}</p>
render() {
return (
<Container class = "container offset-md-3" >
<div className='custom-control custom-switch' >
<input type='checkbox' className='custom-control-input' id='customSwitches' />
<label className='custom-control-label' htmlFor='customSwitches'>
Select Multiple Region's
</label>
{this.props.readings.map((region)=>{
return <Radio_Button region ={region} key ={region.frame_number}/>
})}
Object.keys({this.props.readings}).map((key, index) => {
const myItem = myObject[key]
return <MyComponent myItem={myItem} key={index} />
})
<MDBFormInline>
<input class="region" type="checkbox" name="region1" value="1" />
<label for="region1"> 1</label>
<input class="region" type="checkbox" name="region2" value="2" />
<label for="region2"> 2</label>
</MDBFormInline>
</div>
</Container>
);
}
}
export default Buttons_Footer;
```
Maybe this work
this.props.readings.map((reading, index) => {
Object.keys(reading).map((key, index) => {
const myItem = reading[key]
return <MyComponent myItem={myItem} key={index} />
})
})

Accessing state from another component in react

Just learning React and ran into a problem. I have a selector allowing me to choose year/make/model of a vehicle. However, I want it to reset make and model if the year is changed, or just reset the model if make is changed, so that those options state is set back to null and thus in the ui deselected.
However, the problem is I don't know how to make the state of a component available to the others. I figured the solution could be as simple as using an onChange function for year, that will then take the state of make/model and reset to null, though that's not possible without year.js knowing the state of the other 2...
Hopefully you understand what I'm talking about. Here's the code.
Year.js
import React, { Component } from 'react';
import '../CSS/App.css';
const vehicleYear = [
{id:1,label:"2019",href:"#"},
{id:2,label:"2018",href:"#"},
{id:3,label:"2017",href:"#"},
{id:4,label:"2016",href:"#"},
{id:5,label:"2015",href:"#"},
{id:6,label:"2014",href:"#"}
];
class Year extends Component {
constructor(props){
super(props);
this.state = {
year: null,
}
}
createYearList = () => {
let listItems = [];
for (let i = 0; i < vehicleYear.length; i++) {
listItems.push(
<li className={`list ${this.state.year === vehicleYear[i].id ? "active" : ""}`} onClick={(e) => {
this.yearClick(e, vehicleYear[i].id, vehicleYear[i].label)
}}>
<a href={vehicleYear[i].href}>{vehicleYear[i].label}</a>
</li>
);
}
return listItems;
};
yearClick = (e, id, label) => {
let state = this.state;
state.year = id;
this.setState(state);
console.log(this.state);
console.log(this.props.year);
};
render() {
return (
<div>
{this.createYearList()}
</div>
)
}
}
export default Year;
Make.js
import React, { Component } from 'react';
import '../CSS/App.css';
const vehicleMake = [
{id:1,label:"POLARIS",href:"#"},
{id:2,label:"CAN_AM",href:"#"},
{id:3,label:"YAMAHA",href:"#"},
{id:4,label:"SUZUKI",href:"#"},
{id:5,label:"ARCTIC-CAT",href:"#"}
];
class Make extends Component {
constructor(props){
super(props);
this.state = {
make: null
}
}
createMakeList = () => {
let listItems = [];
for(let i = 0; i < vehicleMake.length; i++){
listItems.push(
<li className={`list ${this.state.make === vehicleMake[i].id ? "active" : ""}`} onClick={(e)=>{this.makeClick(e, vehicleMake[i].id, vehicleMake[i].label)}}>
<a href={vehicleMake[i].href}>{vehicleMake[i].label}</a>
</li>
);
}
return listItems;
};
makeClick = (e, id, label) => {
console.log(id, label);
let state = this.state;
state.make = id;
this.setState(state);
console.log(state.make);
};
render() {
return (
<div>
{this.createMakeList()}
</div>
)
}
}
export default Make;
Model.js
import React, { Component } from 'react';
import '../CSS/App.css';
const vehicleModel = [
{id:1,label:"RZR 570",href:"#"},
{id:2,label:"RZR 900",href:"#"},
{id:3,label:"RZR S 900",href:"#"},
{id:4,label:"RZR S 1000",href:"#"},
{id:5,label:"RZR S 1000 TURBO",href:"#"}
];
class Model extends Component {
constructor(props){
super(props);
this.state = {
model: null
}
}
createModelList = () => {
let listItems = [];
for(let i = 0; i < vehicleModel.length; i++){
listItems.push(
<li className={`list ${this.state.model === vehicleModel[i].id ? "active" : ""}`} onClick={(e)=>{this.modelClick(e, vehicleModel[i].id, vehicleModel[i].label)}}>
<a href={vehicleModel[i].href}>{vehicleModel[i].label}</a>
</li>
);
}
return listItems;
};
modelClick = (e, id, label) => {
console.log(id, label);
let state = this.state;
state.model = id;
this.setState(state);
};
render() {
return (
<div>
{this.createModelList()}
</div>
)
}
}
export default Model;
And here's the main App.js
import React, { Component } from 'react';
import './CSS/App.css';
import { Container, Row, Col } from 'reactstrap';
import rzrPic from './Media/rzr-xp-1000-eps-trails-rocks-media-location-1-xxs.jpg';
import camsoT4S from './Media/camso-atv-t4s.jpg';
import Year from './Components/Year';
import Make from './Components/Make';
import Model from './Components/Model';
class App extends Component {
render() {
return (
<div className="App">
<Container fluid="true">
<Row>
<Col xs="3" className="categories">
<div>
<span className="categoryHeader">
<h2 className="categoryHeading">
VEHICLE YEAR
</h2>
</span>
<div className="categoryList">
<ul>
<Year/>
</ul>
</div>
</div>
<div>
<span className="categoryHeader">
<h2 className="categoryHeading">
VEHICLE MAKE
</h2>
</span>
<div className="categoryList">
<ul>
<Make/>
</ul>
</div>
</div>
<div>
<span className="categoryHeader">
<h2 className="categoryHeading">
VEHICLE MODEL
</h2>
</span>
<div className="categoryList">
<ul>
<Model/>
</ul>
</div>
</div>
</Col>
<Col xs="6" className="fill">
<img src={rzrPic} alt="rzr xp 1000"/>
</Col>
<Col xs="3" className="categories">
<span className="categoryHeader2">
<h2 className="categoryHeading">
AVAILABLE TRACKS
</h2>
</span>
<div className="Track">
<img src={camsoT4S} alt="Camso T4S Tracks"/>
<div className="TrackInfo">
<h3>CAMSO T4S - 4 SEASON</h3>
<p>Starting at $3,999.00</p>
<span>
ADD TO CART
</span>
</div>
</div>
<div className="Track">
<div className="TrackInfo">
<h3>CAMSO T4S - 4 SEASON</h3>
<p>Starting at $3,999.00</p>
<p className="select">SELECT</p>
</div>
</div>
</Col>
</Row>
</Container>
</div>
);
}
}
export default App;
Thanks in advance for your help!
Instead of storing the selected year / make / model in each component, store them in the parent App. You will then handle the reset logic in the App component.
Here is how to refactor your code:
import React, { Component } from "react";
import "../CSS/App.css";
// The list of years is now passed as props
//
// const vehicleYear = [];
class Year extends Component {
// You dont need the constructor anymore as the component
// doesn't have a state to initialize
//
// constructor(props) {}
createYearList = () => {
// Use the year list passed as a prop from the parent
const { vehicleYear } = this.props;
let listItems = [];
for (let i = 0; i < vehicleYear.length; i++) {
listItems.push(
<li
className={`list ${
this.state.year === vehicleYear[i].id ? "active" : ""
}`}
onClick={e => {
this.yearClick(e, vehicleYear[i].id, vehicleYear[i].label);
}}
>
<a href={vehicleYear[i].href}>{vehicleYear[i].label}</a>
</li>
);
}
return listItems;
};
yearClick = (e, id, label) => {
// Call the onClick function passed as a prop from the parent
this.props.onClick(e, id, label);
};
render() {
return <div>{this.createYearList()}</div>;
}
}
export default Year;
I only modified the Year component since the Make and Model components have the same structure. I'll come back to this later.
And here is how to use Year in App:
import React, { Component } from 'react';
// import ...
// Define the list of years
const vehicleYear = [
{id:1,label:"2019",href:"#"},
{id:2,label:"2018",href:"#"},
{id:3,label:"2017",href:"#"},
{id:4,label:"2016",href:"#"},
{id:5,label:"2015",href:"#"},
{id:6,label:"2014",href:"#"}
];
class App extends Component {
constructor(props){
super(props);
// Initialise the state of the App component
this.state = {
year: null,
}
}
// Move the click handler from the Year component to its parent component
yearClick = (e, id, label) => {
this.setState({
year: id
});
};
render() {
return (
<div className="App">
<Container fluid="true">
<Row>
<Col xs="3" className="categories">
<div>
<span className="categoryHeader">
<h2 className="categoryHeading">
VEHICLE YEAR
</h2>
</span>
<div className="categoryList">
<ul>
{/* Pass the list of years, the selected year and the handler to
select a year to the Year component */}
<Year vehicleYear={vehicleYear} selectedYear={this.state.year} onClick={this.yearClick} />
</ul>
</div>
</div>
...
</Row>
</Container>
</div>
);
}
}
export default App;
Now you have a fully controled Year and the logic handled by the App component. If you want to reset the selected year, you only have to create and call such a function in the App component:
resetYear = () => {
this.setState({
year: null
});
};
Bonus: Refactoring
You can refacto your Year, Make and Model components to one reusable component because they have exactly the same structure. Here is a ListComponent extracted from them:
// The list components takes three arguments:
// - itemsList: items to be displayed
// - selectedItemId: the id of the selected item
// - onSelect: a function to call when an item is selected
class ListComponent extends Component {
render() {
const { itemsList, selectedItemId, onSelect } = this.props;
return (
<div>
{itemsList.map(item => (
<li
className={`list ${selectedItemId === item.id ? "active" : ""}`}
onClick={e => {
onSelect(e, item.id, item.label);
}}
>
<a href={item.href}>{item.label}</a>
</li>
))}
</div>
);
}
}
export default ListComponent;
And you can use it like this:
<div>
<span className="categoryHeader">
<h2 className="categoryHeading">
VEHICLE YEAR
</h2>
</span>
<div className="categoryList">
<ul>
<ListComponent onSelect={this.selectYear} itemsList={vehicleYear} selectedItemId={this.state.year}/>
</ul>
</div>
</div>
<div>
<span className="categoryHeader">
<h2 className="categoryHeading">
VEHICLE MAKE
</h2>
</span>
<div className="categoryList">
<ul>
<ListComponent onSelect={this.selectMake} itemsList={vehicleMake} selectedItemId={this.state.make}/>
</ul>
</div>
</div>
<div>
<span className="categoryHeader">
<h2 className="categoryHeading">
VEHICLE MODEL
</h2>
</span>
<div className="categoryList">
<ul>
<ListComponent onSelect={this.selectModel} itemsList={vehicleModel} selectedItemId={this.state.model}/>
</ul>
</div>
</div>
The easiest solution will be to keep your state in the parent in this case in the App.js or create a component ad hoc to be the parent of your components, and simply pass a prop onChangeYear for example to change the state of your parent
import React, { Component } from 'react';
import './CSS/App.css';
import { Container, Row, Col } from 'reactstrap';
import rzrPic from './Media/rzr-xp-1000-eps-trails-rocks-media-location-1-xxs.jpg';
import camsoT4S from './Media/camso-atv-t4s.jpg';
import Year from './Components/Year';
import Make from './Components/Make';
import Model from './Components/Model';
class App extends Component {
contructor(props) {
super(props);
this.state = { year: "" }
}
render() {
return (
<div className="App">
<Container fluid="true">
<Row>
<Col xs="3" className="categories">
<div>
<span className="categoryHeader">
<h2 className="categoryHeading">
VEHICLE YEAR
</h2>
</span>
<div className="categoryList">
<ul>
<Year onChangeYear={(year) => {this.setState({year})/>
</ul>
</div>
</div>
<div>
<span className="categoryHeader">
<h2 className="categoryHeading">
VEHICLE MAKE
</h2>
</span>
<div className="categoryList">
<ul>
<Make/>
</ul>
</div>
</div>
<div>
<span className="categoryHeader">
<h2 className="categoryHeading">
VEHICLE MODEL
</h2>
</span>
<div className="categoryList">
<ul>
<Model/>
</ul>
</div>
</div>
</Col>
<Col xs="6" className="fill">
<img src={rzrPic} alt="rzr xp 1000"/>
</Col>
<Col xs="3" className="categories">
<span className="categoryHeader2">
<h2 className="categoryHeading">
AVAILABLE TRACKS
</h2>
</span>
<div className="Track">
<img src={camsoT4S} alt="Camso T4S Tracks"/>
<div className="TrackInfo">
<h3>CAMSO T4S - 4 SEASON</h3>
<p>Starting at $3,999.00</p>
<span>
ADD TO CART
</span>
</div>
</div>
<div className="Track">
<div className="TrackInfo">
<h3>CAMSO T4S - 4 SEASON</h3>
<p>Starting at $3,999.00</p>
<p className="select">SELECT</p>
</div>
</div>
</Col>
</Row>
</Container>
</div>
);
}
}
export default App;
If you find yourself having a lot of components in your web app I would think to integrate Redux to handle the state globally https://redux.js.org/introduction/getting-started.

Categories

Resources