使用Reactjs获取GET请求 - 如何在我的请求中实现可重用的组件来呈现我的数据?

时间:2017-07-25 21:48:03

标签: javascript reactjs

我已经习惯了一段时间的API,创建了我自己的书籍API来练习。我已经使用html和JavaScript-ES6创建了一个完整的函数库,所有Ajax请求都正常工作。我继续研究Reactjs并尝试使用React构建相同的库。因此,在处理API时,我听说fetch是首选。 所以这是我使用fetch的GET请求:

fetch('http://localhost:8000/api/Books')
        .then(response => {
            if (!response.ok) {
                throw Error('Network request failed.')
            }
            return response;
        })
        .then(data => data.json())
        .then(data => {
            let output = ''
            for (let book of data) {
                output += (
                    <Book id={book._id}
                          title={book.title}
                          author={book.author}
                          genre={book.genre}
                          read={book.read} />
                    );
            }
            console.log('parsed json', data);
            document.getElementById('database').innerHTML = output;
        }, (ex) => {
            this.setState({
                requestError : true
            });
            console.log('parsing failed', ex)
        })

正如您所看到的,Book组件就是将信息作为props获取,并且是父“LibraryProject”组件的子组件。书看起来像这样:

class Book extends React.Component{
constructor (props){
    super(props);
    this.state = {
        edit : false
    }
}
render() {
    return (
        <li data-id={this.props.id}>
            <p><span className="noedit title">{this.props.title}</span>
                <input type="text" className="edit title"/>
            </p>
            <p>Author: <span className="noedit author">{this.props.author}</span>
                <input type="text" className="edit author"/>
            </p>
            <p>Genre: <span className="noedit genre">{this.props.genre}</span>
                <input type="text" className="edit genre"/></p>
            <p>Read: <span className="noedit read">{this.props.read?"Yes":"No"}</span>
                <input type="checkbox" 
                       className="edit read" 
                       value={this.props.read?"true":"false"}/>
            </p>
            <button data-id={this.props.id} className='remove'>Delete</button>
            <button className="editBook noedit">Edit</button>
            <button className="saveEdit edit">Save</button>
            <button className="cancelEdit edit">Cancel</button> 
        </li>

    )
}}

预计会有编辑和删除功能,因此它的结构。

现在,当用户单击按钮时进行调用,因此不使用componentDidMount或任何其他生命周期,请求只是在handleClick函数中实现。

如您所见,我在console.log中请求数据。以及问题开始的地方。数据在控制台中完美显示 - 每个对象及其属性都详细说明:

http://i.imgur.com/LiVRgjw.png

因此,数据已正确接收,并且API本身没有问题。当我看到的所有内容都是[object Object]时,问题出现在网站上:

http://i.imgur.com/Q5jYwp2.png

我试图将Book包装在div中,并且还只是一个带有book.title的div,以查看问题是否与组件呈现有关,但我得到了相同的东西。 只有当我将输出设置为普通的book.title时,没有div或花括号我会逐个获得标题。

.then(data => {
            let output = ''
            for (let book of data) {
                output += (
                    book.title
                    );
            }

所以我的问题:我如何使这项工作?如何在GET请求中实现我的Book组件,以便为数据库中的每个对象呈现可重用的Book?

我在网上搜索了几天,却找不到答案。 我认为这是一个非常基本的障碍,我不是唯一一个面对它的人。 非常感谢你的帮助,期待阅读你的建议!

编辑#1 根据我到目前为止所学到的,需要将来自GET请求的数据存储在容器组件的状态中,并将其作为props传递给表示组件。在我的例子中,它在LibraryProject组件内的componentDidMount()中调用请求,并将其作为props传递给负责显示信息的Book组件。

    //LibraryProject.js 

    componentDidMount () {
            fetch('http://localhost:8000/api/Books')
                .then(response => {
                    if (!response.ok) {
                        throw Error('Network request failed.')
                    }
                    return response;
                })
                .then(data => data.json())
                .then(data => {
                    this.setState({
                        content: data
                    });
                    console.log('parsed json', data);

                }, (ex) => {
                    this.setState({
                        requestError : true
                    });
                    console.log('parsing failed', ex)
                })
        }   
        render () {
            return (
            <div>
                ...

                <div className="book-filter">
                  <button id="search-books"
                          onClick={this.handleSearch.bind(this)}>
                          Search for books
                  </button>
                  ...

                <div id="database">
                    {this.state.database ? <Book list={this.state.content} /> : <h4>Books will be shown here...</h4>}
                    {this.state.requestError ? <h4>Failed to get the data. Please check the log for errors.</h4> : ''}
                </div>
            </div>
        )
    }

    //Book.js
    class Book extends React.Component{
        constructor (props){
            super(props);
            this.state = {
                list : this.props.list
            }
        }
        render() {
            let list = this.state.list;
            for (let book of list){
              return(
                   <li key={book._id}>
                        <p><span className="noedit title">{book.title}</span>
                            ... 
                    </li>
                )
            }
        }
    }

问题现在是我只得到一个从'list'渲染的项目,它是一个对象数组 - [{},{},{}]。 知道为什么吗?

编辑#2 - 最终 好的,所以我设法让它全部工作。 显然,根据React文档:“如果语句和for循环不是JavaScript中的表达式,那么它们就不能直接在JSX中使用”。 所以这是有效的:

        //Book.js
    class Book extends React.Component{
        constructor (props){
            super(props);
            this.state = {
                edit : false,
                list : this.props.list
            }
        }
        componentWillReceiveProps(nextProps) {   //for Updating
            if (nextProps.list !== this.state.list) {
                this.setState({ list: nextProps.list }); 
            }
        }
        render() {
            return(
                <div>
                    {this.state.list.map((book) =>
                        <li key={book._id}>
                            <p><span className="noedit title">{book.title}</span>
                              ... 
                        </li>
                    )}
                </div>)  
        }
    }

我所做的是使用.map作为我的渲染函数中的表达式并将其包装在div中。 希望这对任何人都有帮助,谢谢@Ha Ja的帮助。

2 个答案:

答案 0 :(得分:2)

您应该将api的结果存储在状态中,并在<Book />函数内呈现render

顺便说一句,分开你的组件会更好:

  1. 执行所有逻辑的容器(数据获取,...)。
  2. 呈现ui的表示组件。
  3. 您甚至可以使用redux来处理全局状态,redux-saga来处理副作用(api调用)

    修改

    这是一个小例子。

    演示组成部分:

    const BookListing = ({ books }) => (
      <ul>
        {books.map(book => <li key={book.id}>{book.title}</li>)}
      </ul> 
    );
    

    容器组件:

    class Books extends Component {
      constructor(props) {
        super(props);
        this.state = {books: []};
      }
      componentDidMount() {
        fetch('http://localhost:8000/api/Books')
          .then(data => data.json())
          .then((data) => { this.setState({ books: data }) }); 
      }
      render() {
        return <BookListing books={this.state.books} />;
      }
    }
    

答案 1 :(得分:0)

1)创建一个容器组件,您可以在其中执行AJAX请求,然后将结果保存在本地状态,例如books方法中的render

2)将this.state.books传递到您可以迭代阵列的<Book />组件中。

3)(可选但推荐)。您可以创建另一个组件,如<BookDetail />,以呈现单个图书项目