0

React newbie here.

I tried to slice the data in fetchData() method and trying to access it from the render(). When I try to map the data inside render, it is throwing me a "can't read property map" error. What am I missing? I tried console.info in the fetchData() method and I'm able to see the data is being fetched correctly.

 class BooksComponent extends Component{
        
            constructor(props){
                super(props)
                this.state ={
                    booksData: [],
                    offset: 0,
                    perPage: 3,
                    currentPage: 0,
                }
                this.reserve = this.reserve.bind(this)
                this.fetchData = this.fetchData.bind(this)
            }
        
            fetchData(){
                axios.get('/library')
                  .then(res => {
                    const booksData = res.data
                    const books = booksData.slice(this.state.offset, this.state.offset + this.state.perPage)

                    this.setState({ 
                        pageCount: Math.ceil(booksData.length / this.state.perPage),
                        books })
                  })
            }
            componentDidMount() {
                this.fetchData()
            }
            
            render() {
                return (
                  <div className="App">
                    {this.state.books.map(book => 
                        <React.Fragment key={book.id}>
                        <p>{book.id} - {book.title} - {book.author}</p>
                        <button onClick={() => this.reserve(book.id)}>Reserve {book.quantity}</button>
                        <span>{this.state.booksData.quantity}</span>
                        </React.Fragment>
                    )}
                    <ReactPaginate
                            previousLabel={"prev"}
                            nextLabel={"next"}
                            breakLabel={"..."}
                            breakClassName={"break-me"}
                            pageCount={this.state.pageCount}
                            marginPagesDisplayed={2}
                            pageRangeDisplayed={5}
                            onPageChange={this.handlePageClick}
                            containerClassName={"pagination"}
                            subContainerClassName={"pages pagination"}
                            activeClassName={"active"}/>
                  </div>
                )
            }
        
            reserve(id) {
                console.log("clicked")
                this.setState({
                    booksData: this.state.booksData.map(item => {
                        if (item.id === id) {
                            return { ...item, quantity: (item.quantity - 1) >= 0 ? (item.quantity - 1) : 0};
                        } else {
                            return item;
                        }
                    })
                })
            }
        }
        
        
        export default BooksComponent
1
  • at the time of mounting the API call has not been completed, so books would be undefined. So initializing books to [] empty array might work or you have to do conditional render to check if books has been populated Commented Jul 14, 2020 at 3:34

3 Answers 3

1

In your constructor, initialize this.state.books to an empty array:

constructor(props){
    super(props)
    this.state ={
        books: [],
        booksData: [],
        offset: 0,
        perPage: 3,
        currentPage: 0,
    }
    this.reserve = this.reserve.bind(this)
    this.fetchData = this.fetchData.bind(this)
}
Sign up to request clarification or add additional context in comments.

Comments

0

Add a conditional check inside your return statement. If this.state.books is falsey, then the second statement won't run.

{this.state.books && this.state.books.map(book => 
                        <React.Fragment key={book.id}>
                        <p>{book.id} - {book.title} - {book.author}</p>
                        <button onClick={() => this.reserve(book.id)}>Reserve {book.quantity}</button>
                        <span>{this.state.booksData.quantity}</span>
                        </React.Fragment>
                    )}

edit: also, don't forget to set books to initial state

Comments

0

Try below solution

          {this.state.books ? {this.state.books.map(book => 
                        <React.Fragment key={book.id}>
                        <p>{book.id} - {book.title} - {book.author}</p>
                        <button onClick={() => this.reserve(book.id)}>Reserve 
                      {book.quantity}</button>
                        <span>{this.state.booksData.quantity}</span>
                        </React.Fragment>
             )}: null}

Also check

fetchData(){
                axios.get('/library')
                  .then(res => {
                    const booksData = res.data
                    const books = booksData.slice(this.state.offset, this.state.offset + this.state.perPage)
                  console.log(book)// has data are not
                    this.setState({ 
                        pageCount: Math.ceil(booksData.length / this.state.perPage),
                        books })
                  })
            }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.