I'm learning react and am trying to pass a prop up (I think). The following is my code. When I click 'increment' or 'delete' in the browser, an error message pops up saying 'this.props.onDelete is not a function.' Same with increment; 'this.props.onIncrement is not a function.' Where did I go wrong? Thanks!
App.js
import React, { Component } from "react";
import NavBar from "./components/navbar";
import "./App.css";
import Counters from "./components/counters";
class App extends Component {
state = {
counters: [
{ id: 1, value: 0 },
{ id: 2, value: 0 },
{ id: 3, value: 0 },
{ id: 4, value: 0 },
],
};
render() {
return (
<React.Fragment>
<NavBar />
<main className="container">
<Counters
counters={this.state.counters}
onReset={this.handleReset}
onIncrement={this.handleIncrement}
onDelete={this.handleDelete}
/>
</main>
</React.Fragment>
);
}
}
export default App;
counters.js
import React, { Component } from "react";
import Counter from "./counter";
class Counters extends Component {
handleIncrement = (counter) => {
const counters = [...this.state.counters];
const index = counters.indexOf(counter);
counters[index] = { ...counter };
counters[index].value++;
this.setState({ counters });
};
handleReset = () => {
const counters = this.state.counters.map((c) => {
c.value = 0;
return c;
});
this.setState({ counters });
};
handleDelete = (counterId) => {
const counters = this.state.counters.filter((c) => c.id !== counterId);
this.setState({ counters });
};
render() {
console.log(this.props);
return (
<div>
<button
onClick={this.props.onReset}
className="btn btn-primary btn-sm m-2"
>
Reset
</button>
{this.props.counters.map((counter) => (
<Counter
key={counter.id}
counter={counter}
onDelete={this.props.onDelete}
onIncrement={this.props.onIncrement}
></Counter>
))}
</div>
);
}
}
export default Counters;
counter.js
import React, { Component } from "react";
class Counter extends Component {
doHandleIncrement = () => {
this.handleIncrement({ id: 1 });
};
render() {
console.log(this.props);
return (
<div>
{this.props.children}
<span className={this.getBadgeClasses()}>{this.formatCount()}</span>
<button
onClick={() => this.props.onIncrement(this.props.counter)}
className="btn btn-secondary btn-sm"
>
Increment
</button>
<button
onClick={() => this.props.onDelete(this.props.counter.id)}
className="btn btn-danger btn-sm m-2"
>
Delete
</button>
</div>
);
}
getBadgeClasses() {
let classes = "badge m-2 badge-";
classes += this.props.counter.value === 0 ? "warning" : "primary";
return classes;
}
formatCount() {
const { value } = this.props.counter;
return value === 0 ? "Zero" : value;
}
}
export default Counter;
In App component there are no function defined as handleDelete.
You need to move these function from Counters to App component
handleIncrement = (counter) => {
const counters = [...this.state.counters];
const index = counters.indexOf(counter);
counters[index] = { ...counter };
counters[index].value++;
this.setState({ counters });
};
handleReset = () => {
const counters = this.state.counters.map((c) => {
c.value = 0;
return c;
});
this.setState({ counters });
};
handleDelete = (counterId) => {
const counters = this.state.counters.filter((c) => c.id !== counterId);
this.setState({ counters });
};
These functions are not defined in your App:
onReset={this.handleReset}
onIncrement={this.handleIncrement}
onDelete={this.handleDelete}
Related
I wanna display my cart item inside my CartList component but for some reason, it doesn't work out...Is there anything wrong with the way i destructure the cart variable able or anything? please help me! Thank you so much!
context.js:
class ProductProvider extends React.Component {
state = {
products: storeProducts,
detailProduct: detailProduct,
cart: storeProducts,
modalOpen: false,
modalProduct: detailProduct
};
getItem = (id) => {
const product = this.state.products.find((item) => item.id === id);
return product;
};
addToCart = (id) => {
let tempProducts = [...this.state.products];
const index = tempProducts.indexOf(this.getItem(id));
const product = tempProducts[index];
product.inCart = true;
product.count = 1;
const price = product.price;
product.total = price;
this.setState(() => {
return (
{ products: tempProducts, cart: [...this.state.cart, product] },
() => console.log(this.state)
);
});
};
openModal = (id) => {
const product = this.getItem(id);
this.setState(() => {
return { modalProduct: product, openModal: true };
});
};
closeModal = (id) => {
this.setState(() => {
return { modalOpen: false };
});
};
render() {
return (
<ProductContext.Provider
value={{
...this.state,
addToCart: this.addToCart,
openModal: this.openModal,
closeModal: this.closeModal
}}
>
{this.props.children}
</ProductContext.Provider>
);
}
}
Cart.js:
import React from "react";
import CartColumns from "./CartColumns";
import CartList from "./CartList";
const ProductContext = React.createContext();
export default class Cart extends React.Component {
render() {
return (
<div>
your cart
<ProductContext.Consumer>
{(value) => {
if (value && value.length > 0) {
return (
<div>
<CartColumns />
<CartList value={value}/>
</div>
);
}
}}
</ProductContext.Consumer>
</div>
);
}
}
CartItem.js:
import React from "react";
function CartItem() {
return <div>this is a cart item</div>;
}
export default CartItem;
CartList.js:
import React from "react";
import CartItem from "./CartItem"
export default class CartList extends React.Component {
{value =>{
return <div>
{cart.map((item) => {
return (
<CartItem key={item.id} value={value} item={item}/>
)
}}
</div>;
}}
}
export default CartList;
Sandbox link: https://codesandbox.io/s/cart-code-addict-buz0u?file=/src/cart/CartList.js
first of all fixed your cartList component
import React from "react";
import CartItem from "./CartItem"
export default function CartList (props) {
const {cart} = props
return (
<div>
{cart.map((item) => (
<CartItem key={item.id} item={item} />
))}
</div>
)
}
As, I am new to react I don't know how to perform dynamic add and edit and cancel operations on the textarea. I have dynamic array , i want to perform edit and cancel operations for every textarea individually . If I click on a edit button the mouse cursor should point to the specific textbox, and it should turn into editable mode . If, I click on cancel button the specific textarea should turn into non-editable mode. codesandboxdemo please Run the code in codesandox and give me the solution
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
rootElement
);
App.js
import React, { Component } from "react";
class App extends Component {
constructor() {
super();
this.state = {
count: [],
disabled: false
};
this.newText = {};
this.handleEdit = this.selectText.bind(this);
}
handleCancel(e,index) {
this.setState({disabled:true})
}
handleRemove(index)
{
this.state.count.splice(index,1)
this.setState({count: this.state.count})
}
selectText(e, index) {
newText = this.state.count[index];
console.log(newText);
this.newText.select();
}
add(e) {
this.setState({ count: [...this.state.count, ""] ,disabled:false});
}
handleChange(e, index) {
this.state.count[index] = e.target.value;
this.setState({ count: this.state.count });
}
render() {
return (
<div>
<label>Enter the text</label>
{this.state.count.map((counts, index) => {
return (
<div key={index}>
<input
ref={(newText) => (this.newText = newText)}
onChange={(e) => this.handleChange(e, index)}
value={counts}
disabled = {(this.state.disabled)? "disabled" : ""}
/>
<button onClick={(e) => this.handleEdit(e,index)}>Edit</button>
<button onClick={() => this.handleRemove(index)}>Remove</button>
<button onClick = {(e) =>this.handleCancel(e,index)}> cancel </button>
</div>
);
})}
<button onClick={(e) => this.add(e)}> Add</button>
</div>
);
}
}
export default App;
.App {
font-family: sans-serif;
text-align: center;
}
`]2
I just tried doing it this way for you. This is not a complete answer (just to make sure I don't spoon-feed you, but this is a possible approach). Tell me if this works?
import React, { useState } from "react";
import "./styles.css";
const App = () => {
const [Value, setValue] = useState("");
const [EditMode, setEditMode] = useState(false);
const toggleEditMode = () => setEditMode(!EditMode);
return EditMode ? (
<input
type="text"
value={Value}
onChange={(e) => setValue(e.target.value)}
onBlur={toggleEditMode}
/>
) : (
<span onClick={toggleEditMode}>{Value}</span>
);
};
export default App;
Click and it will make it editable. Come out and it shows updated value.
CodeSandbox: https://c4fog.csb.app/
Here is full working code of App.js
import React, { Component } from "react";
class App extends Component {
constructor() {
super();
this.state = {
count: [],
disabled: [],
};
this.references = []
}
handleRef(r, index) {
this.references[index] = r
}
handleCancel(e,index) {
const { disabled } = this.state;
disabled[index] = true
this.setState({ disabled })
}
handleRemove(index)
{
this.state.count.splice(index,1)
this.setState({count: this.state.count})
}
handleEdit(e, index) {
const { disabled } = this.state;
disabled[index] = false
this.setState({ disabled }, () => {
this.references[index].focus()
})
}
add(e) {
this.setState({ count: [...this.state.count, ""] });
}
handleChange(e, index) {
const { count } = this.state;
count[index] = e.target.value;
this.setState({ count });
}
render() {
const { disabled, count } = this.state
return (
<div>
<label>Enter the text</label>
{count.map((counts, index) => {
return (
<div key={index}>
<input
ref={(newText) => this.handleRef(newText, index)}
onChange={(e) => this.handleChange(e, index)}
value={counts}
disabled ={disabled[index]}
/>
<button onClick={(e) => this.handleEdit(e,index)}>Edit</button>
<button onClick={() => this.handleRemove(index)}>Remove</button>
<button onClick={(e) =>this.handleCancel(e,index)}>Cancel</button>
</div>
);
})}
<button onClick={(e) => this.add(e)}> Add</button>
</div>
);
}
}
export default App;
Here is a link https://codesandbox.io/s/fast-architecture-45u4m?file=/src/Todo.js
I added comments showing which code I am trying to move into a separate component.
So I have this todo app and the original code has all of my functions and logic in one component called TodoList.js
I'm trying to refactor my code, so that all the logic for the todo is in a separate component called Todo.js
Here is what the code looks like
<>
{todos.map(todo => (
<div className='todo-row'>
<div
key={todo.id}
className={todo.isComplete ? 'complete' : ''}
key={todo.id}
onClick={() => completeTodo(todo.id)}
>
{todo.text}
</div>
<FaWindowClose onClick={() => removeTodo(todo.id)} />
</div>
))}
</>
So I have this TodoList.js component with all of my state and functions, but when I tried to remove my todo code, I can't seem to figure out how to refactor it, so that the same data still gets passed in and I am able to use all of my functions again
function TodoList() {
const [todos, setTodos] = useState([]);
const addTodo = todo => {
if (!todo.text || /^\s*$/.test(todo.text)) {
return;
}
const newTodos = [todo, ...todos];
setTodos(newTodos);
console.log(newTodos);
};
const removeTodo = id => {
const removedArr = [...todos].filter(todoId => todoId.id !== id);
setTodos(removedArr);
};
const completeTodo = id => {
let updatedTodos = todos.map(todo => {
if (todo.id === id) {
todo.isComplete = !todo.isComplete;
}
return todo;
});
setTodos(updatedTodos);
};
return (
<>
<TodoForm onSubmit={addTodo} />
<Todo />
</>
);
}
export default TodoList;
Originally, I replaced the component <Todo /> with the code I showed above in the first block. But now I wanna move all of that code into it's own component called Todo.js and then pass it in from there, but I'm running into errors because I have all my functions and state logic inside of the TodoList.js component
As per your sandbox . You just need to pass Props:
Todo.js
// I want to move this code into this component
import React from "react";
import { FaWindowClose } from "react-icons/fa";
const Todo = ({ todos, completeTodo, removeTodo }) => {
return todos.map((todo) => (
<div className="todo-row">
<div
key={todo.id}
className={todo.isComplete ? "complete" : ""}
onClick={() => completeTodo(todo.id)}
>
{todo.text}
</div>
<FaWindowClose onClick={() => removeTodo(todo.id)} />
</div>
));
};
export default Todo;
TodoList.js
import React, { useState } from "react";
import TodoForm from "./TodoForm";
import Todo from "./Todo";
import { FaWindowClose } from "react-icons/fa";
function TodoList({ onClick }) {
const [todos, setTodos] = useState([]);
const addTodo = (todo) => {
if (!todo.text || /^\s*$/.test(todo.text)) {
return;
}
const newTodos = [todo, ...todos];
setTodos(newTodos);
console.log(newTodos);
};
const removeTodo = (id) => {
const removedArr = [...todos].filter((todoId) => todoId.id !== id);
setTodos(removedArr);
};
const completeTodo = (id) => {
let updatedTodos = todos.map((todo) => {
if (todo.id === id) {
todo.isComplete = !todo.isComplete;
}
return todo;
});
setTodos(updatedTodos);
};
return (
<>
<TodoForm onSubmit={addTodo} />
{/* I want to move this code below into a new component called Todo.js */}
<Todo todos={todos} completeTodo={completeTodo} removeTodo={removeTodo} />
</>
);
}
export default TodoList;
Here is the demo : https://codesandbox.io/s/nostalgic-silence-idm21?file=/src/TodoList.js:0-1039
You can pass data and required functions to Todo component through props from TodoList component and Todo component should represent only on Todo item as per name so map should stay in TodoList component so after changes
TodoList.js
function TodoList() {
const [todos, setTodos] = useState([]);
const addTodo = todo => {
if (!todo.text || /^\s*$/.test(todo.text)) {
return;
}
const newTodos = [todo, ...todos];
setTodos(newTodos);
console.log(newTodos);
};
const removeTodo = id => {
const removedArr = [...todos].filter(todoId => todoId.id !== id);
setTodos(removedArr);
};
const completeTodo = id => {
let updatedTodos = todos.map(todo => {
if (todo.id === id) {
todo.isComplete = !todo.isComplete;
}
return todo;
});
setTodos(updatedTodos);
};
return (
<>
<TodoForm onSubmit={addTodo} />
{
todos.map(todo => <Todo to do = {todo} removeTodo={removeTodo} completeTodo={completeTodo}/>)
}
</>
);
}
export default TodoList;
And Todo.js
const {todo} = props;
return (
<div className='todo-row'>
<div
key={todo.id}
className={todo.isComplete ? 'complete' : ''}
key={todo.id}
onClick={() => props.completeTodo(todo.id)}
>
{todo.text}
</div>
<FaWindowClose onClick={() => props.removeTodo(todo.id)} />
</div>
);
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.
I have some trouble with my code. I made an app where I use an API last fm and I want to add a rating button, I get few things from Google. Rating is displayed where I want to be, I can console log him, but it's on external file and I have no idea how to modify rate state from my app.js. Here is my code:
App.js
import React, { Component } from 'react';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import { getArtists } from './services/api';
import {
TextField,
Button,
List
} from '#material-ui/core';
import { ArtistCard } from './components/ArtistCard';
import { SearchResult } from './components/SearchResult';
import './App.css';
import { get } from 'https';
const handleChangeRate = (state) => {
this.setState({rate: state})
}
const isEmpty = (str) => str.length === 0;
class App extends Component {
state = {
searchTerm: '',
savedArtists: [],
rate:[]
}
componentDidMount() {
const existing = localStorage.getItem('savedArtists')
if (existing) {
this.setState({ savedArtists: JSON.parse(existing) })
}
}
onTextChange = (event) => {
const value = event.target.value;
this.setState({ searchTerm: value });
}
search = async (terms) => {
const artists = await getArtists(terms);
this.setState({ artists: artists })
}
onSearchClick = () => {
this.search(this.state.searchTerm);
}
clearSearch = () => {
this.setState({
searchTerm: '',
artists: []
})
}
updateArtists = (newArtists) => {
this.setState({ savedArtists: newArtists })
localStorage.setItem('savedArtists', JSON.stringify(newArtists));
}
deleteArtist = (artist) => {
const result = this.state.savedArtists.filter(item => item.name !== artist.name);
this.updateArtists(result);
}
onResultClick = (artist) => {
this.clearSearch();
const savedArtists = this.state.savedArtists;
savedArtists.push(artist);
this.updateArtists(savedArtists);
}
handleChangeRate = (state) => {
this.setState({rate: state})
}
render() {
const results = this.state.artists || [];
return (
<div className="App">
<header className="App-header">
<AppBar position="static" color="primary">
<Toolbar className="search-bar">
<Typography variant="h6" color="inherit">
Photos
</Typography>
<TextField
placeholder="Search on Last.fm"
className="search-input"
onChange={this.onTextChange}
value={this.state.searchTerm}
/>
<Button
onClick={this.onSearchClick}
variant="contained"
color="secondary"
disabled={isEmpty(this.state.searchTerm)}
>
Search
</Button>
{!isEmpty(this.state.searchTerm) && (
<Button
onClick={this.clearSearch}
variant="contained"
>
Clear
</Button>)
}
</Toolbar>
</AppBar>
</header>
<List className="search-results">
{
results.map((artist, index) => {
return <SearchResult key={index} artist={artist} onResultClick={this.onResultClick} />
})
}
</List>
<div className="artist-container">
{
this.state.savedArtists.map((artist, index) => {
return <ArtistCard artist={artist} key={index} deleteArtist={this.deleteArtist} onChangeRating={this.handleChangeRate} />
})
}
</div>
</div>
);
}
}
export default App;
artistCard.js
import React from 'react';
import { Card, CardContent, CardActions, Button } from '#material-ui/core'
import ReactStars from 'react-stars'
export const ratingChanged = (newRating) => {
const { onChangeRating } = this.props;
onChangeRating(newRating);
}
export const ArtistCard = (props) => {
const { artist, deleteArtist } = props;
console.log(artist.cardImage)
return (
<Card className="artist-card">
<div className="image-container">
<img src={artist.cardImage} alt={artist.name} />
</div>
<CardContent>
<h3>{artist.name}</h3>
<p>{artist.listeners} listeners.</p>
<ReactStars
count = {5}
onChange={ratingChanged}
size={27}
color2 ={'#ffd700'}
/>
</CardContent>
<CardActions>
<Button size="small" color="primary">
Share
</Button>
<Button size="small" color="secondary" onClick={() => deleteArtist(artist)}>
Delete
</Button>
</CardActions>
</Card>
)
}
You need to pass the function to change State to artistCard as props
In App.js add the following fucntion
const handleChangeRate = (state) => {
this.setState(rate: state)
}
and Pass the same as props to ArtistCard like specified
<ArtistCard artist={artist} key={index} deleteArtist={this.deleteArtist} onChangeRating={this.handleChangeRate} />
And in artistCard.js change ratingChanged method to
const ratingChanged = (newRating) => {
const { onChangeRating } = this.props;
onChangeRatng(newRating);
}
PS: This answer is based on the understanding i gained after going through this question, If this is not the requirement Please feel free to comment
EDIT
const handleChangeRate = (state) => {
this.setState(rate: state)
}
try adding the value prop to ReactStart like specified below
<ReactStars
value={this.props.rate}
count={5}
onChange={ratingChanged}
size={24}
color2={'#ffd700'}
/>
Pass rate state to artist card component as prop like specified below
<ArtistCard artist={artist} key={index} deleteArtist= {this.deleteArtist} onChangeRating={this.handleChangeRate} rate={this.state.rate} />
EDIT
cardComponent.js
import React from 'react';
import ReactStars from 'react-stars'
export const ArtistCard = (props) => {
const { artist, deleteArtist, onChangeRating } = props;
console.log(props.rating)
return (
<ReactStars
value={props.rating}
count = {5}
onChange={(newRating) => onChangeRating(newRating)}
size={27}
color2 ={'#ffd700'}
/>)}
App.js
handleChangeRate = (state) => {
this.setState({rate: state})
}
<ArtistCard artist={'artist'} key={'index'} rating={this.state.rate} deleteArtist={this.deleteArtist} onChangeRating={this.handleChangeRate} />
FINAL EDIT
Changes made in your code
App.js
modified state object to
state = {
searchTerm: '',
savedArtists: [],
rate: ''
}
Artistcard component line to
<ArtistCard rate={this.state.rate} artist={artist} key={index} onChangeRating={(val) => {this.setState({ rate: val })}} deleteArtist={this.deleteArtist} />
In ArtistCard.js
rendering reactstart component like this
<ReactStars
value={props.rate}
count = {5}
onChange={(newRating) => onChangeRating(newRating)}
size={27}
color2 ={'#ffd700'}
/>