使用state,setState和render来解决组件问题

时间:2016-11-27 14:34:48

标签: reactjs asynchronous

我正在为研究目的建立一个基于React的项目。我坚持制作一个表组件,它呈现自己,然后向mbaas后端发送ajax请求以获取所有书籍条目并填写新行。这是我到目前为止所提出的。请原谅大块的代码,但由于我还没有完全理解方法之间的交互,因此state和render()在这里是整个类:

class BooksTable extends Component {
    constructor(props) {
        super(props);
        this.state = {
            books: []
        };

        this.updateState = this.updateState.bind(this);
        this.initBooks = this.initBooks.bind(this);
    }

    componentDidMount() {
        let method = `GET`;
        let url = consts.serviceUrl + `/appdata/${consts.appKey}/${consts.collection}`;
        let headers = {
            "Authorization": `Kinvey ${sessionStorage.getItem(`authToken`)}`,
            "Content-Type": `application/json`
        };

        let request = {method, url, headers};
        $.ajax(request)
            .then(this.initBooks)
            .catch(() => renderError(`Unable to connect. Try again later.`));
    }

    deleteBook(id) {
        let method = `DELETE`;
        let url = consts.serviceUrl + `/appdata/${consts.appKey}/${consts.collection}/${id}`;
        let headers = {
            "Authorization": `Kinvey ${sessionStorage.getItem(`authToken`)}`,
            "Content-Type": `application/json`
        };

        let request = {method, url, headers};
        $.ajax(request)
            .then(() => this.updateState(id))
            .catch(() => renderError(`Unable to delete, something went wrong.`));
    }

    updateState(id) {
        for (let entry of this.state.books.length) {
            if (entry.id === id) {
                // Pretty sure this will not work, but that is what I've figured out so far.
                this.state.books.splice(entry);
            }
        }
    }

    initBooks(response) {
        console.log(`#1:${this.state.books});
        console.log(`#2:${this});
        for (let entry of response) {
            this.setState({
                books: this.state.books.concat([{
                    id: entry._id,
                    name: entry.name,
                    author: entry.author,
                    description: entry.description,
                    price: Number(entry.name),
                    publisher: entry.publisher
                }])
            }, () => {
                console.log(`#3${this.state.books}`);
                console.log(`#4${this}`);
            });
        }
    }

    render() {
        return (
            <div id="content">
                <h2>Books</h2>
                <table id="books-list">
                    <tbody>
                        <tr>
                            <th>Title</th>
                            <th>Author</th>
                            <th>Description</th>
                            <th>Actions</th>
                        </tr>
                        {this.state.books.map(x =>
                            <BookRow
                                key={x.id}
                                name={x.name}
                                author={x.author}
                                description={x.description}
                                price={x.price}
                                publisher={x.publisher} />)}
                    </tbody>
                </table>
            </div>
        );
    }
}

现在BookRow不是很有趣,只有onClick部分是相关的。它看起来像这样:

<a href="#" onClick={() => this.deleteBook(this.props.id)}>{owner? `[Delete]` : ``}</a>

如果登录用户不是图书的发布者,则链接不应显示。 onClick调用deleteBook(id),这是BookTable的方法。在成功的ajax上,它应该从state.books(数组)和渲染中删除该书。

我对initBooks方法特别困惑。我在填充状态的循环之前添加了日志,并在状态更新时添加了回调。 log#1和log#3的结果相同,日志#2#4相同。此外,如果我展开log#2(在setState之前)或log#4,那么这两个都显示state = [1]。这有什么意义?此外,如果您查看#1#3日志 - 它们会打印[]。我可能错过了一些内部组件交互,但我真的无法弄清楚是什么。

感谢。

2 个答案:

答案 0 :(得分:1)

setState不会立即更新状态。因此,在for循环的第二次迭代中,您将不会获得新状态。因此,首先制作新书列表,然后在准备好新列表后进行设置。

试试这个:

<base href="/"/>

答案 1 :(得分:1)

试试这个:

initBooks(response = {}) {
    const books = Object.keys(response);

    if (books.length > 0) {
        this.setState((prevState) => {
            const newBooks = books.reduce((acc, key) => {
                const entry = response[key];

                return [ ...acc, {
                    id: entry._id,
                    name: entry.name,
                    author: entry.author,
                    description: entry.description,
                    price: Number(entry.name),
                    publisher: entry.publisher
                }];
            }, prevState.books);

            return { books: newBooks };
        });
    }
}

我在这做了什么?

  • setState仅在需要时(即,只有来自API响应的数据)
  • 避免状态变异,循环内没有setState
  • 使用函数((prevState, props) => newState)来确保原子 更新可靠性。

需要思考的要点:

  • 不要改变你的状态
  • 在循环中避免使用setState;而是准备新的状态对象并进行一次性setState

  • 如果您想在调用setState时访问之前的状态,则advised可以这样做:

    this.setState((prevState, props) => {
      return { counter: prevState.counter + props.increment };
    });
    
      

    setState()不会立即改变this.state但会创建一个   待定状态转换。在调用此文件后访问this.state   方法可以返回现有值。