admin管理员组

文章数量:1331449

TLDR - You should not implement/call your reusable ponent within your data fetching request. The correct way to do so is having a container ponent that is responsible for fetching the data, storing it in state, and passing it down to a presentational ponent that is responsible for rendering the data as UI. (example below)


Initially what I was trying to do (I assumed ponents are converted to HTML and can be created as string - BAD PRACTICE):

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)
        })

My question was:
How do i make this work? how do I Implement my Book ponent inside the GET request to render a reusable Book for every object in the database?

My solution
having <BooksContainer /> as my container ponent books data is stored in state and is iterated using .map to render each book object as a <Book /> ponent - our presentational function.

    //BooksContainer.js - responsible for data fetching and storing
class BooksContainer extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            books: [];
        };
    }
    ponentDidMount () {
        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({
                    books: data
                });
                console.log('parsed json', data);            
            }, (ex) => {
                this.setState({
                    requestError : true
                });
                console.log('parsing failed', ex)
            })
    }   
    render () {
        return (
            <div className="books-container">
                <ul className="books-list">
                    {this.state.books.map(book => <Book {...book} />}
                </ul>
            </div>
        )
    }

    //Book.js - responsible for presenting each book's data
    const Book = (props) => (
        <li>
            <span className="title">{this.props.title}</span>
            <span className="author">Author: {this.props.author</span>
            <span className="genre">Genre: {this.props.genre}</span>
            <span className="read">Read: {this.props.read ? "Yes" : "No"}</span>
        </li>
    )

TLDR - You should not implement/call your reusable ponent within your data fetching request. The correct way to do so is having a container ponent that is responsible for fetching the data, storing it in state, and passing it down to a presentational ponent that is responsible for rendering the data as UI. (example below)


Initially what I was trying to do (I assumed ponents are converted to HTML and can be created as string - BAD PRACTICE):

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)
        })

My question was:
How do i make this work? how do I Implement my Book ponent inside the GET request to render a reusable Book for every object in the database?

My solution
having <BooksContainer /> as my container ponent books data is stored in state and is iterated using .map to render each book object as a <Book /> ponent - our presentational function.

    //BooksContainer.js - responsible for data fetching and storing
class BooksContainer extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            books: [];
        };
    }
    ponentDidMount () {
        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({
                    books: data
                });
                console.log('parsed json', data);            
            }, (ex) => {
                this.setState({
                    requestError : true
                });
                console.log('parsing failed', ex)
            })
    }   
    render () {
        return (
            <div className="books-container">
                <ul className="books-list">
                    {this.state.books.map(book => <Book {...book} />}
                </ul>
            </div>
        )
    }

    //Book.js - responsible for presenting each book's data
    const Book = (props) => (
        <li>
            <span className="title">{this.props.title}</span>
            <span className="author">Author: {this.props.author</span>
            <span className="genre">Genre: {this.props.genre}</span>
            <span className="read">Read: {this.props.read ? "Yes" : "No"}</span>
        </li>
    )
Share Improve this question edited May 18, 2020 at 9:36 Amiry asked Jul 25, 2017 at 21:48 AmiryAmiry 3601 gold badge3 silver badges12 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 4

You should store the result of the api inside the state and render the <Book /> inside the render function.

Btw it would be better to separate your ponents:

  1. The container that do all the logic (data fetching, ...).
  2. The presentational ponent that renders the ui.

You can even go further by using redux for handling a global state, and redux-saga for handling the side effects (api calls)

EDIT

Here is a small example.

The presentational ponent:

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

The container ponent:

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

1) Create a container ponent where you can do your AJAX request and then save the result in your local state, say books in the render method.

2) Pass the this.state.books into your <Book /> ponent where you can iterate over the array.

3) (Optional but remended). You can create another ponent like <BookDetail /> to render individual book item

本文标签: