I have custom list with custom methods. Trying to change list and call setState does not trigger component update
import React, {useState} from "react";
class MyCustomArray extends Array {
push(item) {
super.push(item);
return this;
}
}
export default () => {
const [items, setItems] = useState(new MyCustomArray());
console.log(items)
return (
<div>
{items.length}
<button onClick={() => setItems(items.push(1))}/>
</div>
);
}
I have tried same method using class component instead of function and it works.
import React, {Component} from "react";
class MyCustomArray extends Array {
push(item) {
super.push(item);
return this;
}
}
export default class App extends Component {
constructor(props) {
super(props);
this.state = {items: new MyCustomArray()};
}
render() {
const {items} = this.state;
return (
<div>
{items.length}
<button onClick={() => this.setState({items: items.push(1)})}/>
</div>
);
}
}
You need a new instance of items array, use the spread syntax,
import React, { useState } from "react";
class MyCustomArray extends Array {
push(item) {
super.push(item);
return this;
}
}
export default () => {
const [items, setItems] = useState(new MyCustomArray());
return (
<div>
{items.length}
<button onClick={() => setItems([...items, 1])} />
// ^^^^^^^^^^^^^
</div>
);
};
Same thing if you want to use a class Component :
export default class App extends Component {
constructor(props) {
super(props);
this.state = { items: new MyCustomArray() };
}
updateItems = (item) => {
this.setState(prevState => ({
...prevState,
items: [...prevState.items, item]
}));
}
render() {
const { items } = this.state;
return (
<div>
{items.length}
<button onClick={() => this.updateItems(1)} />
</div>
);
}
}
Related
Can anyone help me explain it, because when use functional it work, but not when use class component
Different class component and func component in handle event react.
fyi it take input text search on child and at parent doing handle event and other
child use functional
import React from "react";
const Header = ({searchTitle,onSearch}) => {
return (
<div>
<header>
<h1>Notes</h1>
<div className="search">
<input
type="text"
placeholder="Search..."
value={searchTitle}
onChange={onSearch}
></input>
</div>
</header>
</div>
);
};
export default Header;
child use class component
import React, { Component } from "react";
export class HeaderPage extends Component {
render() {
const searchTitle = this.props.searchTitle;
const onSearch = this.props.onSearchHandler;
return (
<header>
<h1>Notes</h1>
<div className="search">
<input
type="text"
placeholder="Search..."
value={searchTitle}
onChange={onSearch}
></input>
</div>
</header>
);
}
}
export default HeaderPage;
parent class componet
import React, { Component } from "react";
import HeaderPage from "../organisms/HeaderPage";
import MainPage from "../organisms/MainPage";
import FooterPage from "../organisms/FooterPage";
import { getInitialData } from "../../utils/data";
import Header from "../organisms/Header";
export class PageNote extends Component {
constructor(props) {
super(props);
this.state = {
dataNotes: getInitialData(),
dataNotesFiltered: [],
searchTitle: "",
};
//binding
this.onAddNoteHandler = this.onAddNoteHandler.bind(this);
this.onDeleteHandler = this.onDeleteHandler.bind(this);
this.onArchivedHandler = this.onArchivedHandler.bind(this);
this.onSearchHandler = this.onSearchHandler.bind(this);
}
onAddNoteHandler({ title, body }) {
this.setState((prevState) => {
return {
dataNotes: [
...prevState.dataNotes,
{
id: +new Date(),
title,
body,
archived: false,
createdAt: new Date().toLocaleDateString(),
},
],
};
});
}
onDeleteHandler(id) {
const dataNotes = this.state.dataNotes.filter(
(dataNote) => dataNote.id !== id
);
this.setState({ dataNotes });
}
onArchivedHandler(id) {
const dataNotes = this.state.dataNotes.map((note) => {
if (note.id === id) {
return { ...note, archived: !note.archived };
} else {
return note;
}
});
this.setState({ dataNotes });
}
onSearchHandler(event) {
this.setState(() => {
return {
searchTitle: event.target.value,
};
});
}
render() {
console.log(this.state.dataNotes);
console.log(`search ${this.state.searchTitle}`);
const dataNotes = this.state.dataNotes.filter((note) =>
note.title.toLowerCase().includes(this.state.searchTitle.toLowerCase())
);
return (
<>
<HeaderPage
searchTitle={this.state.searchTitle}
onSearch={this.onSearchHandler}
></HeaderPage>
<Header
searchTitle={this.state.searchTitle}
onSearch={this.onSearchHandler}
></Header>
<MainPage
dataNotes={dataNotes}
addNote={this.onAddNoteHandler}
onDelete={this.onDeleteHandler}
onArchive={this.onArchivedHandler}
></MainPage>
<FooterPage></FooterPage>
</>
);
}
}
export default PageNote;
i have tried to remove todo task from my state array but its not working for me. i am having hard time using deletHandle function in Todolist.js. how can i use key from state to indentify and delete clicked(or perticular) todo task
App.js
import React from 'react';
import { Form } from './components/todo-Form/Form.component';
import { Todolist } from './components/Todolist/Todolist.component';
import './App.css';
class App extends React.Component {
constructor() {
super();
this.state = {
todo: [],
do: ""
}
}
render() {
const handleChange = e => {
this.setState({ do: e.target.value }/*, () => { console.log(e) }*/);
};
const submitHandler = e => {
e.preventDefault();
if (this.state.do != "" ) {
this.setState({ todo: [...this.state.todo, { task: this.state.do, id: Math.random() * 1000 }], do: "" }/*, () => { console.log(this.state.todo) }*/)
}
}
const deletHandler = key => {
this.setState({
todo: this.state.todo.filter(el => el !== key)
})
}
return (
<div className="App">
<header>
<h1>ToDo List</h1>
</header>
<Form handleChange={handleChange} submitHandler={submitHandler} inputText={this.state.do} />
<Todolist check={this.state.todo} deletHandler={deletHandler}/>
</div>
)
}
}
export default App;
Todolist.js
import React from "react";
import { Todo } from "../Todo/Todo.component";
import "./todolist.style.css"
export const Todolist = ({check, deletHandler}) => {
return <div >
<ul className="todo-list">
{check.map(todo => (<Todo task={todo.task} key={todo.id} onClick={() => {deletHandler(todo.id)}}/>))}
</ul>
</div>;
};
Todo.js
import React from "react";
import "./todo.style.css";
export const Todo = ({task}) =>{
return (
<div className="todo-container" >
<li className="todo">
<h2> {task} </h2>
<ul className="delet-x">X</ul>
</li>
</div>)
}
In your delete handler, you have to use the id to delete
const deletHandler = (key) => {
this.setState({
todo: this.state.todo.filter((el) => el.id !== key),
});
};
I am making project, where you get books cards using Google Books API. I need to count amount of books I get in the end and print in in Header.js(after search). I think I need to add new parameter like 'count' in constructor that will get books.length but still don't know how to pass it in the end.
Books.js
import React, { Component } from 'react';
import SearchArea from './SearchArea';
import request from 'superagent';
import BookList from './BookList';
class Books extends Component {
constructor(props){
super(props);
this.state = {
books: [],
searchField: '',
sort: ''
}
}
searchBook = (e) => {
e.preventDefault();
request
.get("https://www.googleapis.com/books/v1/volumes")
.query({ q: this.state.searchField })
.then((data) => {
console.log(data);
const cleanData = this.cleanData(data)
this.setState({ books: cleanData })
})
}
handleSearch = (e) => {
this.setState ({ searchField: e.target.value })
}
handleSort = (e) => {
console.log(e.target.value)
this.setState({sort: e.target.value})
}
cleanData = (data) => {
const cleanedData = data.body.items.map((book) => {
if(book.volumeInfo.hasOwnProperty('publishedDate') === false){
book.volumeInfo['publishedDate'] = '0000';
}
else if(book.volumeInfo.hasOwnProperty('imageLinks') === false) {
book.volumeInfo['imageLinks'] = {thumbnail: 'https://vignette.wikia.nocookie.net/pandorahearts/images/a/ad/Not_available.jpg/revision/latest?cb=20141028171337'}
}
console.log(this.state.books.length)
return book;
})
return cleanedData;
}
render(){
const sortedBooks = this.state.books.sort((a,b) => {
if(this.state.sort === 'Newest') {
return parseInt(b.volumeInfo.publishedDate.substring(0,4)) - parseInt(a.volumeInfo.publishedDate)
}
else if(this.state.sort === 'Oldest') {
return parseInt(a.volumeInfo.publishedDate.substring(0,4)) - parseInt(b.volumeInfo.publishedDate)
}
})
return (
<div>
<SearchArea searchBook = {this.searchBook} handleSearch={this.handleSearch} handleSort={this.handleSort}/>
<BookList books={this.state.books} />
</div>
);
}
}
export default Books;
SearchArea.js
import React from 'react'
const SearchArea = (props) => {
return(
<div className="search-area">
<form onSubmit={props.searchBook} action="">
<input onChange={props.handleSearch} type="text"/>
<button id="search" type="submit">Search</button>
<select defaultValue="Sort" onChange={props.handleSort}>
bled v<option disaalue="Sort">Sort</option>
<option value="Newest">Newest</option>
<option value="Oldest">Oldest</option>
</select>
</form>
</div>
)
}
export default SearchArea;
BookList.js
import React from 'react';
import BookCard from './BookCard';
const BookList = (props) => {
return(
<div className="list">
{
props.books.map((book,i) => {
return <BookCard
key={i}
image={book.volumeInfo.imageLinks.thumbnail}
title={book.volumeInfo.title}
author={book.volumeInfo.authors}
published={book.volumeInfo.publishedDate}
/>
})
}
</div>
)
}
export default BookList;
Header.js
import React from 'react';
const Header = () => {
return(
<header>
<h1>Book Cards</h1>
</header>
)
}
export default Header;
I have been building a trivia game using the Open Trivia API and I am getting an error that I have been trying to figure out but am stuck on. It says: Invalid value for prop value on tag. Either remove it from the element, or pass a string or number value to keep it in the DOM.
I will include my code pages. Thank you in advance for you help.
App.js page:
import React from 'react'
import './App.css'
import axios from 'axios'
import Questions from './components/questions'
import CategorySelector from './components/CategorySelector'
class App extends React.Component {
constructor () {
super()
this.state = {
categories: [],
selectedCategory: null
}
this.selectedCategory = this.selectedCategory.bind(this)
}
componentDidMount () {
axios
.get('https://opentdb.com/api_category.php')
.then(response => {
console.log(response.data)
this.setState({
categories: response.data.trivia_categories
})
})
}
selectedCategory (category) {
this.setState({ selectedCategory: category })
}
render () {
const { categories, selectedCategory } = this.state
return (
<div className='App'>
<h2>Trivia Game</h2>
{
selectedCategory
? <Questions selectedCategory={selectedCategory} />
: (
<CategorySelector
categories={categories}
onSelect={event => selectedCategory}
selectedCategory={this.selectedCategory}
/>
)
}
</div>
)
}
}
export default App
CategorySelector.js page:
import React from 'react'
class CategorySelector extends React.Component {
render () {
const { categories, selectedCategory, onSelect } = this.props
return (
<div className='CategorySelector'>
<select
value={selectedCategory} onChange={onSelect}
>
<option value=''>-- No category selected --</option>
{categories.map(category => (
<option value={selectedCategory} key={category.id}>{category.name}</option>
))}
</select>
</div>
)
}
}
export default CategorySelector
Question.js page
import React from 'react'
import axios from 'axios'
class Questions extends React.Component {
constructor () {
super()
this.state = {
questions: [],
currentQuestionIndex: 0
}
this.handleNextQuestion = this.handleNextQuestion.bind(this)
}
componentDidMount () {
const qUrl = `https://opentdb.com/api.php?amount=3&category=${this.props.selectedCategory.id}`
axios
.get(qUrl)
.then(response => {
console.log(response.data)
this.setState({
questions: response.data.results
})
})
}
handleNextQuestion () {
this.setState({ currentQuestionIndex: this.state.currentQuestionIndex + 1 })
}
render () {
const { selectedCategory } = this.props
const { questions, currentQuestionIndex } = this.state
const currentQuestion = questions[currentQuestionIndex]
let answers = []
if (currentQuestion) {
answers = currentQuestion.incorrect_answers.concat([currentQuestion.correct_answer])
}
return (
<div className='Questions'>
<h2>{selectedCategory.name} Questions</h2>
{currentQuestion && (
<div>
<h3>{currentQuestion.questions}</h3>
<div>
{answers.map((answer, index) => <p key={index}>{answer}</p>)}
</div>
</div>
)}
{(currentQuestionIndex < questions.length - 1) &&
<button onClick={this.handleNextQuestion}>Next Question</button>}
</div>
)
}
}
export default Questions
While passing the prop, you're referring the state variable as this.selectedCategory
Change this line
selectedCategory={this.selectedCategory}
to
selectedCategory={selectedCategory}
I am trying to build a simple todolist app on react and I came up this problem. I could write the code necessary for adding todo items to the list defined in the state and displaying it but when it comes to deleting items off of the list from the click of the button adjacent to every todoitem, all todoitems get removed from the list except the first item. what should I do. Here's the code :
The TodoList(parent) Component:
import React, {Component} from 'react';
import AddTodos from './AddTodos';
import DisplayTodos from './DisplayTodos'
import "./TodoList.css";
class TodoList extends Component {
constructor(props){
super(props);
this.state = {
myList: [],
searchField: ''
}
this.DeleteTodos = this.DeleteTodos.bind(this)
}
onSearchChange = (event) => {
this.setState({searchField: event.target.value})
}
addTodo = () => {
if(this.state.searchField !=="") {
var newItem = {
text: this.state.searchField,
key: Date.now()
}
this.setState({myList: this.state.myList.concat(newItem)})
this.setState({searchField: ""})
console.log(this.state.myList)
} else{
alert("Please enter something")
}
}
DisplayList = () => {
const ListItems = this.state.myList.map((listitem, i) => {
return(
<li key={listitem.key} onClick={(i) => this.DeleteTodos(i)}>{listitem.text} <button>Delete</button>
</li>
)
})
return ListItems;
}
DeleteTodos = (i) => {
i.preventDefault()
this.setState({myList: this.state.myList.splice(i, 1)})
}
render() {
return(
<div className="TodoList">
<AddTodos addTodo={this.addTodo} onSearchChange={this.onSearchChange}/>
<DisplayTodos DisplayList={this.DisplayList}/>
</div>
)
}
}
export default TodoList;
The DisplayTodos Component:
import React, {Component} from 'react';
import AddTodos from './AddTodos';
import DisplayTodos from './DisplayTodos'
import "./TodoList.css";
class TodoList extends Component {
constructor(props){
super(props);
this.state = {
myList: [],
searchField: ''
}
this.DeleteTodos = this.DeleteTodos.bind(this)
}
onSearchChange = (event) => {
this.setState({searchField: event.target.value})
}
addTodo = () => {
if(this.state.searchField !=="") {
var newItem = {
text: this.state.searchField,
key: Date.now()
}
this.setState({myList: this.state.myList.concat(newItem)})
this.setState({searchField: ""})
console.log(this.state.myList)
} else{
alert("Please enter something")
}
}
DisplayList = () => {
const ListItems = this.state.myList.map((listitem, i) => {
return(
<li key={listitem.key} onClick={(i) => this.DeleteTodos(i)}>{listitem.text} <button>Delete</button>
</li>
)
})
return ListItems;
}
DeleteTodos = (i) => {
i.preventDefault()
this.setState({myList: this.state.myList.splice(i, 1)})
}
render() {
return(
<div className="TodoList">
<AddTodos addTodo={this.addTodo} onSearchChange={this.onSearchChange}/>
<DisplayTodos DisplayList={this.DisplayList}/>
</div>
)
}
}
export default TodoList;
AddTodos Component
import React, {Component} from 'react';
import AddTodos from './AddTodos';
import DisplayTodos from './DisplayTodos'
import "./TodoList.css";
class TodoList extends Component {
constructor(props){
super(props);
this.state = {
myList: [],
searchField: ''
}
this.DeleteTodos = this.DeleteTodos.bind(this)
}
onSearchChange = (event) => {
this.setState({searchField: event.target.value})
}
addTodo = () => {
if(this.state.searchField !=="") {
var newItem = {
text: this.state.searchField,
key: Date.now()
}
this.setState({myList: this.state.myList.concat(newItem)})
this.setState({searchField: ""})
console.log(this.state.myList)
} else{
alert("Please enter something")
}
}
DisplayList = () => {
const ListItems = this.state.myList.map((listitem, i) => {
return(
<li key={listitem.key} onClick={(i) => this.DeleteTodos(i)}>{listitem.text} <button>Delete</button>
</li>
)
})
return ListItems;
}
DeleteTodos = (i) => {
i.preventDefault()
this.setState({myList: this.state.myList.splice(i, 1)})
}
render() {
return(
<div className="TodoList">
<AddTodos addTodo={this.addTodo} onSearchChange={this.onSearchChange}/>
<DisplayTodos DisplayList={this.DisplayList}/>
</div>
)
}
}
export default TodoList;
DeleteTodos = (i) => {
i.preventDefault();
let arr = [].concat(this.state.myList);
arr.splice(i, 1);
this.setState({myList: arr});
}
The splice function will change the array and return a new array of deleted items, so you should declair a variable to copy myList, and change the variable. What you do will change the state.