即使在调用setState之后,React.js子状态也不会重新呈现?

问题描述 投票:1回答:2

我有一个带有一个子组件的应用程序,当setState更新父级状态的bookInput时,我想重新渲染它。我正在使用axios从谷歌的书api请求信息。出于某种原因,即使状态正在更新,孩子也不会重新渲染。如果可以的话请帮忙!谢谢!

import React, { Component } from 'react';
import axios from 'axios';

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      bookInput: 'ender',
      bookSubmitted: 'initial'
    }

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleSubmitEmpty = this.handleSubmitEmpty.bind(this);
  }

    handleChange(e) {
    this.setState({bookInput: e.target.value});
    console.log(this.state.bookInput);
    //this.setState({bookSubmitted: false});
  }

  handleSubmit(e) {
    e.preventDefault();
    //this.setState({bookSubmitted: true})
    const name = this.state.bookInput;
    this.setState({bookInput: name});
    console.log(this.state);
    this.setState({bookSubmitted: 'userSub'});
  }

  handleSubmitEmpty(e) {
    alert('please enter an item to search for');
    e.preventDefault();
  }

  render() {

    return (

      <div className="App">
        <header className = "App-header">
          <h1>Book Search App</h1>
        </header>

        <form className = "form-style" onSubmit = {this.state.bookInput ? this.handleSubmit: this.handleSubmitEmpty}>
          <label>
            <input type="text" className = "input-style" 
              value = {this.state.bookInput} onChange = {this.handleChange}>
            </input>
          </label>
          <button type="submit">search books</button>
        </form>
        {/* <Book bookInput = {this.state.bookInput}/> */}
        {/*this.state.bookSubmitted && <Book bookInput = {this.state.bookInput}/>*/}
        {
          (this.state.bookSubmitted === 'initial' || this.state.bookSubmitted === 'userSub') && 
          <Book bookInput = {this.state.bookInput}/>
        } 
      </div>
    );
  }
}

export default App;


class Book extends Component {
  constructor(props) {
    super(props);

    this.state = {
      //bookInput2: "ender",
      bookTitles: [],
      bookExample: '',
      isLoading: false
    }

    this.bookClick = this.bookClick.bind(this);
  }

  bookClick(book) {
    console.log(book);
    console.log(book.volumeInfo.infoLink);
    const bookURL = book.volumeInfo.infoLink;
    window.open(bookURL);
  }

  componentDidMount() {
    //this.setState({ isLoading: true });
    this.setState({isLoading: true});
    axios.get(`https://www.googleapis.com/books/v1/volumes?q=${this.props.bookInput}`)
      .then((response) => {
        const bookExample1 = response.data.items;
        console.log(bookExample1);
        this.setState({bookTitles: bookExample1, isLoading: false});
      })
      .catch((error) => {
        console.error('ERROR!', error);
        this.setState({isLoading: false});
      });
  }

  render() {
    return (
      <div>
        { this.state.bookTitles ? (
          <div>
            <h2>book list</h2>
            {<ul className = 'list-style'>
              {this.state.isLoading && 
                (<div>
                  loading book list
                </div>)
              }
              {this.state.bookTitles.map(book => (
                <li key={book.id}>
                  <span className = 'book-details book-title' onClick = {() => this.bookClick(book)}> {book.volumeInfo.title}</span>
                  <br/>
                  {book.volumeInfo.imageLinks && 
                    <img src = {book.volumeInfo.imageLinks.thumbnail}/>
                  }
                  { book.volumeInfo.description && 
                    <span className = 'book-details'>{book.volumeInfo.description}</span>
                  }
                  <br/>
                  <span className = 'book-details'>Categories {book.volumeInfo.categories}</span>
                </li>
              ))}
            </ul>}
        </div>) :
        (<p>sorry, that search did not return anything</p>)}
      </div>
    );
  }
}
reactjs components react-component
2个回答
1
投票

也许你正在寻找类似的东西? https://stackblitz.com/edit/react-snoqkt?file=index.js

上面的代码可以更简单,更有条理,但它给你一些想法。

代码中的主要变化。

  1. 将componentDidMount生命周期事件中的Api调用更改为在handleSubmit中调用的名为getInitialdata的新方法。 getInitialdata(name){ axios.get(`https://www.googleapis.com/books/v1/volumes?q=${name}`) .then((response) => { const bookExample1 = response.data.items; console.log(bookExample1); this.setState({bookTitles: bookExample1, isLoading: false, bookSubmitted: 'userSub'}); }) .catch((error) => { console.error('ERROR!', error); this.setState({isLoading: false, bookSubmitted: 'userSub'}); }); }
  2. 改变了Child组件的使用方式。 <Book bookTitles={this.state.bookTitles} isLoading={this.state.isLoading}/>

您的代码问题是您正在组件的didMount方法中进行API调用。仅在装入组件时才会调用此生命周期事件。不是在更新时。当您在文本框中输入一些输入并单击“搜索书籍”时,componentDidMount事件不会触发。这就是为什么API调用不是第二次发生的原因。

更多关于https://reactjs.org/docs/react-component.html#componentdidmount的生命周期事件


0
投票

我已经把你的代码外推到this sandbox。正如您所说,您的父组件状态正在更新,但问题是子组件不会更改其状态。

状态更改将始终在React中触发重新呈现。唯一的问题是,您的子组件正在管理它自己的状态,而不是直接改变。相反,它只是一次又一次地接收新的道具,但却没有对它们做任何事情。

如果你看一下<Book />组件的代码,你只需在componentDidMount上修改它的状态,它只发生一次。如果您想以编程方式对其进行更新,则可以执行以下两种操作之一。

  1. 从子组件中删除状态,并使其完全依赖于props,以便它与父组件保持同步
  2. 使用componentDidUpdate生命周期方法(docs)选择何时更改子项的状态(将触发重新呈现)
© www.soinside.com 2019 - 2024. All rights reserved.