Where do I add a click event in reactjs - 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.

Related

React native search issues - search results return 'undefined'

I'm trying to build a movie search app using the OMDB API. I have the majority of the app working as desired - except for the search functionality. Every time I go to search, the result will display as the movie 'undefined' (surprisingly a real movie).
Search result displays as the movie 'undefined'
Does anyone have any ideas why? I'm thinking it has everything to do with the line this.props.onSearch(e.target.value); in handleSubmit but I'm not sure what to change there. Please forgive any super obvious mistakes - I'm a react newbie.
Here is my code:
<!-----Search.js----->
import React from 'react';
import PropTypes from 'prop-types';
import Constants from 'expo-constants';
import MoviesList from './MoviesList';
class Search extends React.Component {
state = {
searchText: ''
}
onSearchChange = e => {
this.setState({
searchText: e.target.value
});
}
handleSubmit = e => {
e.preventDefault();
this.props.onSearch(e.target.value);
e.currentTarget.reset();
console.log(e.target.value);
// alert('search submitted!')
}
render() {
return(
<form className="search-form" onSubmit={this.handleSubmit}>
<input type="search"
onChange={this.onSearchChange}
name="search"
value={this.state.searchText}
placeholder="Search..." />
<button className="search-button" type="submit" id="submit">Go!</button>
</form>
);
}
}
export default Search;
<!------MoviesList.js----->
import React from 'react';
import Constants from 'expo-constants';
import PropTypes from 'prop-types';
import Search from './Search';
const MoviesList = props =>
<ul>
{props.results.map((result, index) =>
<li key={index}>
<b>{result.Title}</b> - <strong>{result.Year}</strong>
<p>{result.Plot}</p>
</li>
)}
</ul>
export default MoviesList;
<!----App.js----->
import React from 'react';
import {Button, SectionList, StyleSheet, Text, View, FlatList } from 'react-native';
import Constants from 'expo-constants';
import Search from './Search'
import MoviesList from './MoviesList'
const API_KEY = "xxxx";
export default class App extends React.Component {
constructor() {
super();
this.state = {
results: [],
isLoading: true,
movie: "tt3896198"
};
this.APIURL = `http://www.omdbapi.com/?page=3&t=${this.state.searchText}&apikey=${API_KEY}`;
}
searchMovies = (t = 'inception') => {
// alert('search movies!')
fetch(this.APIURL)
.then(response => response.json())
.then(data => {
const updateMovies = this.state.results;
updateMovies.push(data);
this.setState({
results: updateMovies,
})
})
.catch(error => {
console.log('Error fetching and parsing', error);
});
// console.log(this.state.results);
}
getMovies = async() => {
alert('get movies!')
fetch(`http://www.omdbapi.com/?&i=${
this.state.movie}&apikey=${API_KEY}`)
.then(resp => resp.json())
.then(data => {
console.log(data)
const importMovies = this.state.results;
importMovies.push(data);
this.setState({
results:importMovies,
})
});
}
componentWillMount() {
this.getMovies()
}
render() {
return (
<div className = "App">
<Search onSearch = {this.searchMovies} />
<div>
{
(this.state.loading) ? <p>Loading</p> :<MoviesList results={this.state.results} />
}
</div>
</div>
);
}
}

Filter values from state

AutoComplete Class
import React from 'react';
import axios from 'axios';
import DeleteItem from '../DeleteItem/DeleteItem'
export default class AutoComplete extends React.Component{
constructor(props){
super(props)
this.state={
suggestions:[],
text:'',
persons:[],
delsuggestions:[],
}
this.delete = this.delete.bind(this);
}
// Here I am calling api
componentDidMount() {
axios.get(`https://jsonplaceholder.typicode.com/users`)
.then(res => {
const persons = res.data;
this.setState({ persons:persons });
})
}
//on entering values
onTextChanged= (e) =>{
const value=e.target.value;
let suggestions=[];
if(value.length>0){
suggestions=this.state.persons.map(item =>(item.name))
.filter(function(val){
return val.indexOf(value) > -1
})
}
this.setState(()=>({suggestions:suggestions,text:value}))
}
//On selecting a value from listitems
personSelected(value){
this.setState(()=>({
text:value,
persons:[],
}))
}
//delete function
handleDelete=id =>{
let delsuggestions=[];
this.setState(()=>({delsuggestions:[...this.state.suggestions].filter(el => el!= id)}))
console.log('delsuggestions'+delsuggestions)
}
//listing out filtered items
renderPersons(){
const {persons}=this.state
return(
<React.Fragment>
{this.state.suggestions.map(item => (
<DeleteItem
key={item}
searchitem={item}
onDelete={this.handleDelete}
/>
))}
</React.Fragment>
)
}
//render function
render(){
const {text} = this.state;
return(<div>
<input value={text} onChange={this.onTextChanged} type='text' style={{backgroundColor:'pink'}} />
{this.renderPersons()}
</div>)
}
}
DeleteItem Class
import React, { Component } from "react";
class DeleteItem extends Component {
render() {
return (
<div className="deletebuttom">
{this.props.searchitem} <button
onClick={() => this.props.onDelete(this.props.searchitem)}
>
Delete
</button>
</div>
);
}
}
export default DeleteItem;
what's problem with the below filter function
handleDelete=id =>{
let delsuggestions=[];
this.setState(()=>({delsuggestions:this.state.suggestions.filter(el => el.id != id)}))
console.log('delsuggestions'+delsuggestions)
}
I am not getting filtered values in delsuggestions.When I debug "unexpected end of input" is displayed onhover of el.

React Router/Context API Search Component

I used to make this code work out for my search component but after the on submit is called, I receive this error which never happened before, does anyone have any clue???
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
import React, { Component } from "react";
import axios from "axios";
import { Redirect } from "react-router-dom";
import { Consumer } from "../context";
class Search extends Component {
constructor() {
super();
this.state = {
productTitle: "",
apiUrl: "*******************************",
redirect: false
};
}
findProduct = (dispatch, e) => {
e.preventDefault();
axios
.post(
`${this.state.apiUrl}`,
JSON.stringify({ query: this.state.productTitle })
)
.then(res => {
dispatch({
type: "SEARCH_TRACKS",
payload: res.data.output.items
});
this.setState({ items: res.data.output.items, redirect: true });
})
.catch(err => console.log(err));
};
onChange = e => {
this.setState({ [e.target.name]: e.target.value });
};
render() {
const { redirect } = this.state;
if (redirect) {
return <Redirect to="/searchresult" />;
}
return (
<Consumer>
{value => {
const { dispatch } = value;
return (
<div>
<form onSubmit={this.findProduct.bind(this, dispatch)}>
<div className="form-group" id="form_div">
<input
type="text"
className="form-control form-control-md"
placeholder="...محصولات دسته یا برند مورد نظرتان را انتخاب کنید"
name="productTitle"
value={this.state.productTitle}
onChange={this.onChange}
/>
<button className="btn" type="submit">
<i className="fas fa-search" />
</button>
</div>
</form>
</div>
);
}}
</Consumer>
);
}
}
import React, { Component } from 'react'
import axios from 'axios'
const Context = React.createContext();
export const axiosDashboard = () => {
const URL = (`*****************`);
return axios(URL, {
method: 'POST',
data: JSON.stringify({refresh:"true"}),
})
.then(response => response.data)
.catch(error => {
throw error;
});
};
const reducer = (state, action) => {
switch(action.type){
case 'SEARCH_TRACKS':
return {
...state,
items: action.payload,
heading: 'Search Results'
};
default:
return state;
}
}
export class Provider extends Component {
state = {
dispatch:action => this.setState(state => reducer(state, action))
}
render() {
return (
<Context.Provider value={this.state}>
{this.props.children}
</Context.Provider>
)
}
}
export const Consumer = Context.Consumer
import React, { Component } from 'react'
import { Consumer } from '../context'
import SearchResult from './SearchResult'
import './Search.css'
class Tracks extends Component {
render() {
return (
<Consumer>
{value => {
const { items } = value
if(items === undefined || items.length === 0){
return 'hello'}
else{
return(
<React.Fragment>
<div id='products_search'>
<div className='container'>
<div className="row justify-content-end">
{items.map(item => (
<SearchResult
key={item.id}
id={item.id}
title={item.name}
current_price={item.current_price}
lowest_price={item.lowest_price}
store_name={item.store_name}
thumb={item.thumb_url}/>
))}
</div>
</div>
</div>
</React.Fragment>
)
}
}}
</Consumer>
)
}
}
export default Tracks
import React from 'react'
import {Link} from 'react-router-dom'
import './Search.css'
const SearchResult = (props) => {
const {title,current_price,lowest_price,thumb,id,store_name} = props
return (
<div className="col-md-3" id="searchresult">
<img src={thumb} alt=""/>
<div className="sexy_line"></div>
<p className="muted">{store_name}</p>
<h6>{title}</h6>
<p>{lowest_price}</p>
<Link to={`products/item/${id}`}>
<button type="button" className="btn btn-light rounded-pill">{
new Intl
.NumberFormat({style: 'currency', currency: 'IRR'})
.format(current_price)
}</button>
</Link>
</div>
)
}
export default SearchResult
Maximum update depth exceeded.
This means that you are in a infinit loop of re rendering a component.
The only place where I can see this is possible to happen is in this part
if (redirect) {
return <Redirect to="/searchresult" />;
}
Maybe you are redirecing to the a route that will get the same component that have the redirect.
Please check if you aren't redirecting to the same route as this component and provide your routes and what is inside Consumer.

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

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')} />

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