I am passing the state of the parent component from the parent component to the child component.And in the child component,I have a different state.I am performing some actions on the child component's state and the result of that has to be added to the parent component's state.So,in my parent component I have written a callback function which will update the state of the parent component.The code is:
updateState = (booksList) => {
this.setState({books : this.state.books.push(booksList)});
}
So,this function is then passed to the child component as props:
<BookSearch
books={this.state.books}
handleShelfChange={this.handleShelfChange}
updateState={this.updateState}/>
Then in my child component,I am trying to implement the callback function as :
let getBook = this.state.books.filter(filteredBook => filteredBook.shelf !== "none")
this.props.updateState(getBook)
But this is not working as expected.Is this the correct way?Can anyone please help me with this?
I have tried to solve my problem by implementing the solution provided here : How to pass data from child component to its parent in ReactJS? , but I am getting some errors.
EDIT
Parent component : App.js
import React from 'react'
import * as BooksAPI from './BooksAPI'
import { Link } from 'react-router-dom'
import { Route } from 'react-router-dom'
import './App.css'
import BookList from './BookList'
import BookSearch from './BookSearch'
class BooksApp extends React.Component {
constructor(props) {
super(props);
this.state = {
books: [],
showSearchPage : false
};
//this.updateState = this.updateState.bind(this)
}
componentDidMount() {
BooksAPI.getAll().then((books) => {
this.setState({ books })
})
console.log(this.state.books);
}
filterByShelf = (bookName,shelfName) =>
bookName.filter(book => book.shelf===shelfName)
isTheBookNew = book => {
let is = false;
if (book.shelf === "none")
{ this.setState(state =>
{
books: state.books.push(book)});
is = true;
console.log(this.state.books);
}
return is;
};
handleShelfChange = (bookOnChange, newSehlf) => {
!this.isTheBookNew(bookOnChange) && this.setState(state => {
let newBooks = state.books.map(book =>
{ if (bookOnChange.id === book.id)
{ book.shelf = newSehlf; }
return book;
});
return {
books: newBooks
};
}
);
BooksAPI.update(bookOnChange, newSehlf);
};
updateState = (booksList) => {
const books = [...this.state.books, booksList]
this.setState({ books });
}
render() {
return (
<div className="app">
<Route exact path="/" render={() => (
<div className="list-books">
<div className="list-books-title">
<h1>MyReads</h1>
</div>
<BookList
books={this.filterByShelf(this.state.books,'currentlyReading')}
shelfName='Currently Reading'
handleShelfChange={this.handleShelfChange}/>
<BookList
books={this.filterByShelf(this.state.books,'wantToRead')}
shelfName='Want to Read'
handleShelfChange={this.handleShelfChange}/>
<BookList
books={this.filterByShelf(this.state.books,'read')}
shelfName='Read'
handleShelfChange={this.handleShelfChange}/>
<div className="open-search">
<Link
to="./search" />
</div>
</div>
)
} />
<Route path="/search" render={() =>
<BookSearch
books={this.state.books}
handleShelfChange={this.handleShelfChange}
updateState={this.updateState}/>
} />
</div>
)
}
}
export default BooksApp
BookSearch.js :
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import escapeRegExp from 'escape-string-regexp'
import sortBy from 'sort-by'
import * as BooksAPI from './BooksAPI'
import BookList from './BookList'
class BookSearch extends Component {
constructor(props) {
super(props);
this.state = {
search:'',
books:[]
}
}
updateSearch = (searchString) => {
this.setState({search: searchString.trim()})
let searchResults = BooksAPI.search(this.state.search,1).then((book_search) => {
if (book_search != undefined) {
console.log(book_search);
book_search.map((book) => book.shelf = 'none');
this.setState({ books : book_search }, this.check); // callback function to this.setState
console.log(this.state.books)
}
})
}
check = () => {
let parent_books = this.props.books;
console.log(this.state.books)
const book_result = this.state.books.map((book) => {
const parent = parent_books.find(parent => parent.title === book.title );
if(parent) {
//console.log(parent);
book.shelf = parent.shelf;
//console.log(book)
}
return book;
})
this.setState({books: book_result}, () => {console.log(this.state.books)})
}
updateParentState = () => {
let getBook = this.state.books.filter(filteredBook => filteredBook.shelf !== "none")
this.props.updateState(getBook)
}
render() {
return(
<div className="search-books">
<div className="search-books-bar">
<Link
to="/"
className="close-search">
Close
</Link>
<div className="search-books-input-wrapper">
<input
type="text"
placeholder="Search by title or author"
value={this.state.search}
onChange={(event) => this.updateSearch(event.target.value)}/>
</div>
</div>
<div className="search-books-results">
<ol className="books-grid">
<BookList
books={this.state.books}
handleShelfChange={this.props.handleShelfChange}
updateParentState={this.updateParentState}/>
</ol>
</div>
</div>
)
}
}
export default BookSearch
BookList.js
import React, { Component } from 'react';
import Book from './Book'
class BookList extends Component {
constructor(props) {
super(props);
this.state = {
showSearchPage : false
}
console.log(this.props.books)
}
render() {
return(
<div className="app">
<div>
<div className="list-books-content">
<div>
<div className="bookshelf">
<h2 className="bookshelf-title">{this.props.shelfName}</h2>
<div className="bookshelf-books">
<ol className="books-grid">
{this.props.books.map(book =>
<li key={book.title}>
<Book
book={book}
handleShelfChange={this.props.handleShelfChange}
update={this.props.updateParentState} />
</li>)
}
</ol>
</div>
</div>
</div>
</div>
</div>
</div>
)
}
}
export default BookList;
Book.js
import React, { Component } from 'react'
class Book extends Component {
constructor(props) {
super(props);
this.props.updateParentState;
}
render() {
return(
<div className="book">
<div key={this.props.book.title}>
<div className="book-top">
<div className="book-cover" style={{width:128, height:193, backgroundImage: `url(${this.props.book.imageLinks.thumbnail})`}}>
<div className="book-shelf-changer">
<select id="bookName" value={this.props.book.shelf}
onChange={(event) => this.props.handleShelfChange(this.props.book, event.target.value)}>
<option value="moveTo" disabled>Move to...</option>
<option value="currentlyReading">Currently Reading</option>
<option value="wantToRead">Want to Read</option>
<option value="read">Read</option>
<option value="none">None</option>
</select>
</div>
</div>
</div>
<div className="book-title">{this.props.book.title}</div>
<div className="book-authors">{this.props.book.authors}</div>
</div>
</div>
)
}
}
export default Book
So, I have 4 components as shown above.From App component,I am calling BookSearch component where I can search for books and send the books to App component by selecting the value in dropdown. Each book in BookSearch component will initially be assigned a shelf property of "none".When a user selects a shelf value from Booksearch,the book will automatically be added to the App component.So,when I navigate back to the App component from BookSearch,I should be able to see the book in the assigned shelf.So,for this case,I am using the updateSearch function. The books are displayed through the Book component which has the dropdown value.