node.js - react 应用程序的状态与我数据库中的状态不一致?

标签 node.js reactjs axios

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

问题是,除非我刷新页面,否则我的状态仍然显示一个空的“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;

最佳答案

与每次组件状态更改时都会调用的 render() 不同,componentDidMount() 仅在组件“挂载”后调用一次,也就是说,在其 DOM Node 插入到 DOM 中之后。有关“已安装”术语的更多详细信息,您可能需要查看 Component Lifecyle Reference .

在您的代码中,添加项目将修改您客户端的 state.items (顺便说一下,不是 state.currentUser.shoppingList),以及您的 fetchUser 函数作为 componentDidMount 客户端方法的一部分仅被调用一次(引用: https://reactjs.org/docs/react-component.html#componentdidmount )。

换句话说,当您使用 addItem 将项目添加到数据库时,您的组件将不会与数据库更新同步,因为组件的状态不会更新以反射(reflect)数据库中发生的更改。后端。

如果您想将用户与数据库中的内容同步,那么您可以在将项目添加到数据库后调用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)
        });
      });
  }

抱歉,我没有测试代码。也许更简洁的方法是“ promise ”您的 addItem 函数,就像您对 fetchUser 所做的那样。

希望这有帮助!

关于node.js - react 应用程序的状态与我数据库中的状态不一致?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47726063/

相关文章:

node.js - 如何使用 nodejs 提供图像

javascript - 如何在Rails路由器之上使用React Router(不使用react-rails gem)?

javascript - 如何使用 React 访问嵌套的 JSON 对象数据?

javascript - 使用 Axios 在 Microsoft Teams 中发布消息

javascript - 多个请求的axios拦截器刷新 token

reactjs - 从 http 源获取数据到 https 源

node.js - 在数据库中存储大文本数据

node.js - 使用 NodeJS 创建 CLI - 将变量传递给 exec linux 命令

javascript - 如何以编程方式读取和解析 gruntfile 以便我可以修改它并再次保存?

javascript - 重用具有不同样式的 React 组件