反应应用程序状态与我在数据库中的状态不一致?

时间:2017-12-09 06:41:51

标签: node.js reactjs axios

我正在开发一个小型购物清单应用程序。每当我提交表单时,我都会执行axios请求将项目添加到数据库并更新我的“items”状态以包含新的杂货项目。

问题是,除非我刷新页面,否则我的状态仍会显示一个空的“items”数组。 “items”数组的初始状态是从数据库中提取的,这可以通过使用componentDidMount并在其中设置“items”的状态来实现。

此外,在我的州,我有currentUser,它也从数据库中提取。如果我在添加第一个项目后尝试记录this.state.currentUser.shoppingList,它将显示一个空数组,即使我可以看到服务器端日志表明数组不为空。

这是我的代码:

**App Component**
import React from 'react';
import ListForm from './ListForm';
import * as helpers from '../helpers';

class App extends React.Component{
  state = {
    currentUser: {},
    items: []
  }

  componentDidMount() {
    console.log('Hello!');
    helpers.fetchUser()
      .then(data => {
        this.setState({
          currentUser: data,
          items: data.shoppingList
        }, () => {
          console.log(this.state)
        });
      });
  }

  // Handle adding new items
  onSubmit = (id, item) => {
    this.setState({items: this.state.items.concat([item])});
    helpers.addItem(id, item);
    console.log(this.state);
  }

  // Handle deletion of items
  onDelete = (id, deleteItem) => {
    var shoppingList = this.state.currentUser.shoppingList;
    var index = shoppingList.findIndex(x => x.name === deleteItem);

    console.log(shoppingList);
    helpers.removeItem(id, deleteItem);
  }

  render() {
    return (
      <div className="container row offset-4">
        <div className="jumbotron col-sm-6">
          <ListForm
            currentUser={this.state.currentUser}
            items={this.state.items}
            onSubmit={this.onSubmit}
            onDelete={this.onDelete}
          />
        </div>
      </div>
    );
  }
};

export default App;

Helpers.js

import axios from 'axios';

export const fetchUser = async () => {
  const resp = await axios.get('/api/current_user');

  return resp.data;
}

export const addItem =  (id, newItem) => {
  const resp = axios.post("/api/" + id + "/addItem", newItem);

  return resp.data;
}

export const removeItem = (id, deleteItem) => {
  const resp = axios.delete("/api/" + id + "/removeItem", {data: {item: deleteItem}});

  return resp.data;
}

ListForm组件

import React from 'react';
import ListItem from './ListItem';

class ListForm extends React.Component {
  state = {
    value: ''
  }

  // Render content based on whether or not currentUser has been returned from
  // our API request.
  renderContent = () => {
    const shoppingList = this.props.currentUser.shoppingList;
    if(shoppingList === null || shoppingList === undefined) {
      return (<h5>Loading List</h5>);
    } else {
        return (
          <div>
            {this.props.items.map((item, index) =>
              <ListItem
                {...item}
                key={index}
                id={index}
                currentUser={this.props.currentUser}
                onDelete={this.props.onDelete}
              />
            )}
          </div>
        );
    }
  }

  // Handle the submission of a new item to database and state.
  handleSubmit = e => {
    e.preventDefault();

    this.props.onSubmit(this.props.currentUser._id, {name: this.state.value});
    this.setState(prevState => ({value: ''}));
  }

  // Handle any changes within the input.
  onChange = e => {
    this.setState({value: e.target.value});
  }

  render() {
    return (
      <div className="col-xs-9">
          <h3>Grocery List</h3>
        <form className="form-control" onSubmit={this.handleSubmit}>
          <input style={{display: "inline", width: "60%", height: "2em"}} className="form-control" type="text"
            value={this.state.value}
            onChange={this.onChange}
          />
          <button className="btn btn-success btn-sm float-right">Add item</button>
        </form>
        <div style={{marginTop: "10%"}}>
          {this.renderContent()}
        </div>
      </div>
    );
  }
}

export default ListForm;

ListItem组件

import React from 'react';

const ListItem = props => {

  // Handle delete item requests
  const handleClick = event => {
    var delItem = document.getElementById("btn" + props.id).textContent;

    props.onDelete(props.currentUser._id, delItem);
  }

  return (
    <div>
      <li
        style={{display: "inline"}} id={"btn" + props.id} key={props.id}>{props.name}
      </li>
      <button
        style={{display: "inline"}} onClick={e => handleClick(e)} className="btn btn-danger btn-sm float-right">
          Remove
      </button>
      <hr />
    </div>
  );
}

export default ListItem;

2 个答案:

答案 0 :(得分:2)

与每次更改组件状态时调用的render()不同,componentDidMount()仅在组件被“挂载”之后,即在插入其DOM节点之后被调用一次在DOM中。有关“已装载”字词的详细信息,您可能需要查看Component Lifecyle Reference

在您的代码中,添加项目会修改客户端的state.items(顺便说一下,不是state.currentUser.shoppingList),而fetchUser函数只会被调用一次,因为它是{{{ 1}}客户端方法(参考:  https://reactjs.org/docs/react-component.html#componentdidmount)。

换句话说,当您使用componentDidMount将项添加到数据库时,您的组件将不会与数据库更新同步,因为组件的状态未更新以反映后端中发生的更改。 / p>

如果您想将您的用户与数据库中的内容同步,那么您可以在将项目添加到数据库后调用addItem,如下所示:

fetchUser

我没有测试代码,抱歉。也许更清洁的方式是“宣传” // Handle adding new items onSubmit = (id, item) => { this.setState({items: this.state.items.concat([item])}); helpers.addItem(id, item); console.log(this.state); helpers.fetchUser() .then(data => { this.setState({ currentUser: data, items: data.shoppingList }, () => { console.log(this.state) }); }); } 功能,就像你对addItem所做的那样。

希望这有帮助!

答案 1 :(得分:1)

而不是:

onSubmit = (id, item) => {
    this.setState({items: this.state.items.concat([item])});
    helpers.addItem(id, item);
    console.log(this.state);
  }

试试这个:

onSubmit = (id, item) => {
    this.setState({items: [...this.state.items,item]});
    helpers.addItem(id, item);
    console.log(this.state);
  }