Why am I getting `this.props.onAdd is not a function`? - reactjs

I'm creating a simple CRUD React app that let's you manage products. Products only have a name and a price.
In my AddProduct component, my onSubmit method can successfully log this.nameInput.value, this.priceInput.value, but when I change the log to this.props.onAdd I get this.props.onAdd is not a function.
I'm following a tutorial, so I'm sure I'm missing one small thing, but could use another set of eyes on my code.
Here's my addProduct component - the onSubmit method has the this.props.onadd(...):
import React, { Component } from 'react';
class AddProduct extends Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit(event) {
event.preventDefault();
this.props.onAdd(this.nameInput.value, this.priceInput.value);
}
render() {
return (
<form onSubmit={this.onSubmit}>
<h3>Add Product</h3>
<input placeholder="Name" ref={nameInput => this.nameInput = nameInput}/>
<input placeholder="Price" ref={priceInput => this.priceInput = priceInput}/>
<button>Add</button>
<hr />
</form>
);
}
}
And here's my App.js:
import React, { Component } from 'react';
import './App.css';
import ProductItem from './ProductItem';
import AddProduct from './AddProduct';
const products = [
{
name: 'iPad',
price: 200
},
{
name: 'iPhone',
price: 500
}
];
localStorage.setItem('products', JSON.stringify(products));
class App extends Component {
constructor(props) {
super(props);
this.state = {
products: JSON.parse(localStorage.getItem('products'))
};
this.onAdd = this.onAdd.bind(this);
this.onDelete = this.onDelete.bind(this);
}
componentWillMount() {
const products = this.getProducts();
this.setState({ products });
}
getProducts() {
return this.state.products;
}
onAdd(name, price) {
const products = this.getProducts();
products.push({
name,
price
});
this.setState({ products })
}
onDelete(name) {
const products = this.getProducts();
const filteredProducts = products.filter(product => {
return product.name !== name;
});
this.setState({ products: filteredProducts });
}
render() {
return (
<div className="App">
<h1>Products Manager</h1>
<AddProduct
/>
{
this.state.products.map(product => {
return (
<ProductItem
key={product.name}
{...product}
onDelete={this.onDelete}
/>
);
})
}
</div>
);
}
}
export default App;
What's the matter with my code? When I click the Add button, I get the error.

It's because you pass no prop to the <AddProduct /> you render.
You should add it like so:
<AddProduct onAdd={this.onAdd}/>

You need to pass the props to the component:
<AddProduct onAdd={onAdd} />

Just change the following line
<form onSubmit={this.onSubmit}>
to
<form onSubmit={this.onSubmit.bind(this)}>

onAdd is a field in the props of AddProduct. It means you should delivery a function to AddProduct in App. like: <AddProduct onAdd={() => console.log('works')} />

Related

Cannot get input value on submit function

I am trying to display input value on submit. But it seems to be not working. I don't have any errors but nothing being rendered. What is wrong with the code?
import React from 'react';
import { Component } from 'react';
class App extends Component {
constructor (props) {
super(props)
this.state = {
city : ""
}
}
handleSubmit = (event)=> {
event.preventDefault();
this.setState ({
city : event.target.value
})
}
render () {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input type = "text" city = "city_name" />
<button type="submit">Get Weather</button>
{this.state.city}
</form>
</div>
)
}
}
export default App;
<form
onSubmit={e=>{
e.preventDefault()
console.log(e.target[0].value)
}}>
<input type="text"/>
<button type="submit">dg</button>
</form>
that works for me very well
Remember onSubmit target:
Indicates where to display the response after submitting the form. So you can get inner elements (and their corresponding values) like normal javascript code.
const city = event.target.querySelector('input').value
handleSubmit = (event) => {
event.preventDefault();
this.setState ({ city })
}
I guess you want it to get work like below. But this is not the only way to get it done.
import React from "react";
import { Component } from "react";
class App extends Component {
constructor(props) {
super(props);
this.state = {
city: ""
};
}
handleSubmit = (event) => {
const formData = new FormData(event.currentTarget);
event.preventDefault();
let formDataJson = {};
for (let [key, value] of formData.entries()) {
formDataJson[key] = value;
}
this.setState(formDataJson);
};
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input type="text" name="city" />
<button type="submit">Get Weather</button>
{this.state.city}
</form>
</div>
);
}
}
export default App;
Code sandbox => https://codesandbox.io/s/eager-oskar-dbhhu?file=/src/App.js

duplicates being created in todo list app

I am creating a todo list where when the user clicks the checkbox "complete" that is next to the todo item, it appears in the complete component however there is a duplicate of that item that is being added as well and i am also having an issue trying to have the checkbox not appear in the completed component...
When a user creates a new todo it appears in the active component first and it has a checkbox next to it called completed and when the user clicks the checkbox it appears in the completed component
import React from 'react';
import Active from './Components/Active';
import Completed from './Components/Completed';
import Todoform from './Components/Todoform';
import './App.css';
class App extends React.Component {
state = {
items: [],
task: '',
id: 0,
completedItems: []
}
handleInput = (event) => {
this.setState({
task: event.target.value
})
}
handleSubmit = (event) => {
event.preventDefault()
const newTask = {
id: this.state.id,
title: this.state.task
}
const updatedItems = [...this.state.items, newTask]
this.setState({
items: updatedItems,
task: '',
id: this.state.id + 1
})
}
handleComplete = (newTask) => {
this.setState({completedItems: [...this.state.items, newTask]})
//console.log(this.state.items)
}
render() {
return (
<div id="main-content">
<h1>Task Lister</h1>
<Todoform
handleChange={this.handleInput}
handleSubmit={this.handleSubmit}
task={this.state.task}
/>
<Active
items={this.state.items}
handleComplete={this.handleComplete}
/>
<Completed
completedItems={this.state.completedItems}
/>
</div>
)
}
}
export default App;
import React from 'react'
class Todo extends React.Component{
state = {
checked: false
}
handleCheck = () => {
this.setState({
checked: !this.state.checked
})
}
handleClick = () => {
this.props.handlecompletedList(this.props.title)
}
render(){
const { title } = this.props
return (
<div className="ui checked checkbox">
<input type="checkbox" checked={this.state.checked} onChange={this.handleCheck}
onClick={this.handleClick}/>
<label>Completed {title}</label>
</div>
)
}
}
export default Todo;
import React from 'react'
import Todo from './Todo'
const Active = (props) => {
const { items, handleComplete } = props
return(
<div id="activeList">
<h2 className="position">Active</h2>
<ul id="tasks">
{
items.map(item => {
return(
<Todo key={item.id} handlecompletedList={handleComplete} title={item.title}/>
)
})
}
</ul>
</div>
)
}
export default Active;
import React from 'react'
import Todo from './Todo'
const Completed = (props) => {
const { completedItems } = props
return(
<div id="completedList">
<h2 className="position">Completed</h2>
<ul id="tasks">
{
completedItems.map(item => {
return(
<Todo key={item.id} title={item.title}/>
)
})
}
</ul>
</div>
)
}
export default Completed
import React from 'react';
class Todoform extends React.Component {
render(){
const {task, handleChange, handleSubmit} = this.props;
return(
<form onSubmit={handleSubmit}>
<label>Task description:</label>
<input type="text" name="name" placeholder="description" value={task} onChange={handleChange}/>
<button>Create New Task</button>
</form>
)
}
}
export default Todoform;
To hide the checkbox next to completed items you need to use Conditional Rendering. An example would be to add a prop IsCompleted to your component and use it when rendering html like this:
{this.props.isCompleted &&
<input
type="checkbox"
checked={this.state.checked}
onChange={this.handleCheck}
onClick={this.handleClick}/>
}
The duplicate item issue is probably because you use this.state.items in your handleComplete method instead of using this.state.completedItems if this is not the issue, would you mind sharing the code for the Todoform component as well?
EDIT: The item duplicates because when the handleComplete is called it copies this.state.items to the list and adds the one that you clicked on.
You should use this.state.completedItems in the handleComplete, also you are currently only sending and appending the title in the handleComplete method, you should be appending an object that has a title. The solution would be to update your handleClick method to this and update handleComplete to use this.state.completedItems:
handleClick = () => {
this.props.handlecompletedList({
title: this.props.title
});
};

App.js is not defined in react project

I am building a project in reactJS framework and when I had one big class App i decided to divide into a few classes. After changes I can see below error
'App' is not defined
Can anybody help me with this problem?
I tried all webpack settings but it doesn't help. It appears only after dividing the class 'App' but, before it was working fine.
Here is my code.
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props){
super(props);
this.state = {
list,
searchTerm: "",
};
this.onDismiss = this.onDismiss.bind(this);
this.onSearchChange = this.onSearchChange.bind(this);
}
onSearchChange(event){
this.setState({ searchTerm: event.target.value });
}
onDismiss(id) {
const isNotId = item => item.objectID !== id;
const updatedList = this.state.list.filter(isNotId);
this.setState({ list: updatedList });
}
render() {
const { searchTerm, list } = this.state;
return (
<div className="App">
<Search
value = {searchTerm}
onChange = {this.onSearchChange}
/>
<Table
list = {list}
pattern = {searchTerm}
onDismiss = {this.onDismiss}
/>
</div>
);
}
}
class Search extends Component {
render(){
const { value, onChange } = this.props;
return(
<form>
<input
type = "text"
value = "value"
onChange = {onChange}
/>
</form>
);
}
}
class Table extends Component {
render(){
const { list, pattern, onDismiss } = this.props;
return(
<div>
{list.filter(isSearched(pattern)).map(item =>
<div key={item.objectID}>
<span>
<a href={item.url}>{item.title}</a>
</span>
<span>{item.author}</span>
<span>{item.num_comments}</span>
<span>{item.points}</span>
<span>
<button onClick={() => onDismiss(item.objectID)} type="button">
Delete
</button>
</span>
</div>
)}
</div>
);
}
};
}
export default App;
The answer you'll need is here
Few things I would like to explain. Check my comments in the code below
import React, { Component } from 'react';
import './App.css'; // have proper naming conventions change it to lowercase app.css
export default class App extends Component {
constructor(props){
super(props);
this.state = {
list,
searchTerm: "",
};
//Manual binding are ok but if you use arrow function you can stay away with scope related issues like let that = this;
//this.onDismiss = this.onDismiss.bind(this);
//this.onSearchChange = this.onSearchChange.bind(this);
}
onSearchChange = (event) => {
this.setState({ searchTerm: event.target.value });
}
onDismiss = (id) => {
const isNotId = item => item.objectID !== id;
const updatedList = this.state.list.filter(isNotId);
this.setState({ list: updatedList });
}
render() {
const { searchTerm, list } = this.state;
return (
<div className="App"> //Follow naming conventions chang classname App to app
<Search
value = {searchTerm}
onChange = {this.onSearchChange}
/>
<Table
list = {list}
pattern = {searchTerm}
onDismiss = {this.onDismiss}
/>
</div>
);
}
}
//you need to export your component to make it available to other components
export class Search extends Component {
render(){
const { value, onChange } = this.props;
return(
<form>
<input
type = "text"
value = "value"
onChange = {onChange}
/>
</form>
);
}
}
//you need to export your component to make it available to other components
export class Table extends Component {
render(){
const { list, pattern, onDismiss } = this.props;
return(
<div>
{list.filter(isSearched(pattern)).map(item =>
<div key={item.objectID}>
<span>
<a href={item.url}>{item.title}</a>
</span>
<span>{item.author}</span>
<span>{item.num_comments}</span>
<span>{item.points}</span>
<span>
<button onClick={() => onDismiss(item.objectID)} type="button">
Delete
</button>
</span>
</div>
)}
</div>
);
}
};
}

Where do I add a click event in reactjs

Maybe this doesen't even look like a code, but is there any way I can change other components value/state on click?
import React from 'react';
import './pokemonList.css';
import {Component} from 'react';
import Pokemon from './Pokemon';
class PokemonList extends Component {
constructor(props){
super(props);
this.state = {
pokemons : [],
pokemon : {}
};
}
componentWillMount(){
fetch('https://pokeapi.co/api/v2/pokemon/').then(res=>res.json())
.then(response=>{
this.setState({
pokemons : response.results,
});
});
}
handleClick(id) {
fetch(`https://pokeapi.co/api/v2/pokemon/${id}/`)
.then(res => res.json())
.then(data => {
const pokemon = new Pokemon(data);
this.setState({ pokemon: pokemon });
})
.catch(err => console.log(err));
console.log("click happened");
}
render(){
const {pokemons} = this.state;
return (
<div className='pokemonList'> {pokemons.map(pokemon =>(
<button onClick={this.handleClick.bind(this)} className='pokemon-
btn' key={pokemon.name}>
{pokemon.name}
</button>
))}
</div>
)
}}
export default PokemonList;
At this point I'm not even sure where does handleClick() has to be, so I put it in my App component aswell. The output is ok, but clicking these buttons doesen't seem to do anything. They are supposed to show detailed pokemon information in component.
import React, {Component} from 'react';
import './pokemon-info.css';
const PokemonInfo = ({ pokemon }) => {
const { name,
height,
weight,
sprite,
statsSpeed,
statsSpecialDefense,
statsSpecialAttack,
statsDefense,
statsAttack,
statsHp
} = pokemon;
return (
<section className="pokemonInfo">
<img src={sprite} className='sprite-image' alt="pokemon_sprite"/>
<div className='data-wrapper'>
<h3 className="data-char">{pokemon.name}</h3><br />
<p className = 'data-char'>Height: {height}</p>
<p className = 'data-char'>Weight: {weight}</p><br />
<p className = 'data-char'>Stats: </p><br />
<p className = 'data-char'>Speed: {statsSpeed}</p>
<p className = 'data-char'>Special defense: {statsSpecialDefense}</p>
<p className = 'data-char'>Special attack: {statsSpecialAttack}</p>
<p className = 'data-char'>Defense: {statsDefense}</p>
<p className = 'data-char'>Attack: {statsAttack}</p>
<p className = 'data-char'>Hp: {statsHp}</p>
</div>
</section>
)
}
export default PokemonInfo;
Here is my App component
import React, { Component } from 'react';
import './App.css';
import PokemonList from './PokemonList';
import Pokemon from './Pokemon';
import PokemonInfo from './PokemonInfo';
class App extends Component {
constructor() {
super();
this.state = {
pokemon: {}
};
this.handleOnClick = this.handleOnClick.bind(this);
}
handleOnClick(id) {
fetch(`http://pokeapi.co/api/v2/pokemon/${id}/`)
.then(res => res.json())
.then(data => {
const pokemon = new Pokemon(data);
this.setState({ pokemon });
})
.catch(err => console.log(err));
}
render() {
return (
<div className="App">
<PokemonList />
<PokemonInfo pokemon={this.state.pokemon}/>
</div>
);
}
}
export default App;
It is obvious I did go wrong somewhere, but where?
Update:
Pokemon
class Pokemon {
constructor(data) {
this.id = data.id;
this.name = data.name;
this.height = data.height;
this.weight = data.weight;
this.sprite = data.sprites.front_default;
this.statsSpeed = data.stats[0].stats.base_stat;
this.statsSpecialDefense = data.stats[1].stats.base_stat;
this.statsSpecialAttack = data.stats[2].stats.base_stat;
this.statsDefense = data.stats[3].stats.base_stat;
this.statsAttack = data.stats[4].stats.base_stat;
this.statsHp = data.stats[5].stats.base_stat;
}
}
export default Pokemon;
Your App component should keep the state and pass updater functions as props to children components:
PokemonList
import React from "react";
import "./pokemonList.css";
import { Component } from "react";
import Pokemon from "./Pokemon";
class PokemonList extends Component {
render() {
const { pokemons } = this.props;
return (
<div className="pokemonList">
{pokemons.map(pokemon => (
<button
onClick={() => this.props.handleClick(pokemon.id)} // id or whatever prop that is required for request
className="pokemon-btn"
key={pokemon.name}
>
{pokemon.name}
</button>
))}
</div>
);
}
}
PokemonInfo - no change here.
APP
import React, { Component } from "react";
import "./App.css";
import PokemonList from "./PokemonList";
import Pokemon from "./Pokemon";
import PokemonInfo from "./PokemonInfo";
class App extends Component {
constructor() {
super();
this.state = {
pokemon: {},
pokemons: [],
};
this.handleOnClick = this.handleOnClick.bind(this);
}
componentDidMount() {
fetch("https://pokeapi.co/api/v2/pokemon/")
.then(res => res.json())
.then(response => {
this.setState({
pokemons: response.results
});
});
}
handleOnClick(id) {
fetch(`http://pokeapi.co/api/v2/pokemon/${id}/`)
.then(res => res.json())
.then(data => {
const pokemon = new Pokemon(data);
this.setState({ pokemon });
})
.catch(err => console.log(err));
}
render() {
return (
<div className="App">
<PokemonList pokemons={this.state.pokemons} handleClick={this.handleOnClick} />
<PokemonInfo pokemon={this.state.pokemon} />
</div>
);
}
}
More on lifting the state up.

Why is my method Render Props of the React Component not working?

I have a problem. I'm trying do the method Render Prop but it not is working.
My project is: It has to render some names of ComponentDidMount, and I can get it to do the filter and to filter the names. But I passed the function filter for a component, and do the Render Prop.
I pass it here:
import React from 'react';
import './Body.css';
import { Link } from "react-router-dom";
import axios from 'axios';
import Filter from './Filter';
class Body extends React.Component {
constructor(props) {
super(props);
this.state = {
employee: []
}
}
componentDidMount() {
axios
.get("http://127.0.0.1:3004/employee")
.then(response => this.setState({ employee: response.data }));
}
getName = (filter) => {
const { employee, add } = this.state;
return employee.filter(employee => employee.name.includes(filter)).map(name => (
<div className='item' key={name.id}>
<Link className="link" to={`/user/${name.id}`}>
<div key={name.id}>
<img className="img" alt="imgstatic"
src={`https://picsum.photos/${name.id}`}
/>
</div>
<h1 className="name2"> {name.name} </h1>
</Link>
</div>
));
};
getValueInput = (evt) => {
const inputValue = evt.target.value;
this.setState({ input: inputValue });
}
render() {
return (
<div>
<h4 className="manager"> Hello {this.props.currentManager}, here be all employees available for change. </h4>
<div className="body">
{this.getName()}
</div>
<div className='input'>
<Filter render={this.getName} />
</div>
</div>
)
}
}
export default Body;
And here I get him:
import React from 'react';
class Filter extends React.Component {
constructor() {
super();
this.state = {
input: ''
}
}
getValueInput = (evt) => {
const inputValue = evt.target.value;
this.setState({ input: inputValue });
console.log();
console.log(this.state.input)
}
render() {
return (
<div>
<input placeholder='Search name here' type="text" onChange={this.getValueInput} />
</div>
)
}
}
export default Filter
But something's not working...
Can someone help me?
You are not at all using the render prop being supplied to the Filter component. Also the objective of render prop is to render the data, go using this.getName() inside the render Body Component isn't correct either(for one you are not passing the filter value to the getName). You would use it like
import React from 'react';
import './Body.css';
import { Link } from "react-router-dom";
import axios from 'axios';
import Filter from './Filter';
class Body extends React.Component {
constructor(props) {
super(props);
this.state = {
employee: []
}
}
componentDidMount() {
axios
.get("http://127.0.0.1:3004/employee")
.then(response => this.setState({ employee: response.data }));
}
getName = (filter) => {
const { employee, add } = this.state;
return employee.filter(employee => employee.name.includes(filter)).map(name => (
<div className='item' key={name.id}>
<Link className="link" to={`/user/${name.id}`}>
<div key={name.id}>
<img className="img" alt="imgstatic"
src={`https://picsum.photos/${name.id}`}
/>
</div>
<h1 className="name2"> {name.name} </h1>
</Link>
</div>
));
};
getValueInput = (evt) => {
const inputValue = evt.target.value;
this.setState({ input: inputValue });
}
render() {
return (
<div>
<h4 className="manager"> Hello {this.props.currentManager}, here be all employees available for change. </h4>
<div className='body'>
<Filter render={this.getName} />
</div>
</div>
)
}
}
export default Body;
and Filter as
import React from 'react';
class Filter extends React.Component {
constructor() {
super();
this.state = {
input: ''
}
}
getValueInput = (evt) => {
const inputValue = evt.target.value;
this.setState({ input: inputValue });
console.log();
console.log(this.state.input)
}
render() {
return (
<React.Fragment>
{this.props.render(this.state.input)}
<div className='input'>
<input placeholder='Search name here' type="text" onChange={this.getValueInput} />
</div>
</React.Fragment>
)
}
}
Note React.Fragment is available from v16.2.0 onwards and if you are not using the relevant version replace React.Fragment with <div>

Resources