在使用 Redux 从我的商店/状态中删除项目后,如何让我的视图更新 [英] How do I get my view to update after an item was removed from my store/State in React with Redux

查看:32
本文介绍了在使用 Redux 从我的商店/状态中删除项目后,如何让我的视图更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 React 和 Redux 的新手,刚刚对管理状态和 React 的总体想法感到满意.我先介绍一下,因为我可能需要在我所拥有的背景下可能的解决方案 - 基本上这样我会更好地理解它.:)

据说这是我的问题:

我在 React 中创建了一个列表/表单组件,但遇到了两个明显的问题.

当该项目被数据库删除时,它仅在刷新时反映在视图中您可能已经注意到,从列表中删除项目时,列表 # 或 ID 列不会减去.我在后端使用 PostgreSQL,使用 Sequelize 作为我的对象/关系映射器,并为我的视图/组件使用 React.

我提供了一个 gif 以便你们都能明白我的意思.

提前致谢!

这是我的代码:

反应:Student.js

import React, { Component } from "react";从../store"导入商店;import { deleteStudent } from "../reducers";导出默认类学生扩展组件{构造函数(道具){超级(道具);this.state = store.getState();this.deleteStudent = this.deleteStudent.bind(this);}componentDidMount() {this.unsubscribe = store.subscribe(() => {this.setState(store.getState());});}componentWillUnmount() {this.unsubscribe();}删除学生(索引){store.dispatch(deleteStudent(index));this.setState(store.getState());}使成为() {var student = this.props.students;返回 (<div className="容器"><div className="十六列"><h1 className="remove-bottom">学生</h1><h5>在校学生名单及其校园</h5><小时/>

<div className="十六列"><div className="示例"><div><table className="u-full-width"><头><tr><th>#</th><th>姓名</th><th>电子邮件</th><th>校园</th></tr></thead>{students.map(function(student, index) {返回 (<tr key={index}><td>{学生卡}</td><td>{学生姓名}</td><td>{student.email}</td><td>{student.campus}</td><td>

解决方案

删除学生后,您正在分派动作,并通过动作创建者 scrubStudent 进行分派.您正在该动作创建者中传递已删除学生的 ID.现在你定义你的动作创建者的方式是这样的

导出函数scrubStudent(student){返回 {类型:DELETE_STUDENT,学生};}

所以这个函数的返回值将是一个像这样的对象

scrubStudent(5)//返回 {type: "DELETE_STUDENT", student: 5}

但是在您的减速器中,您正在比较这样的 id

case DELETE_STUDENT://console.log("action.student", action.student);//console.log("state", state);newState = state.students.filter(function(student) {返回 student.id !== action.id;});返回新状态;

在上面的代码中 action.id 是未定义的.而是将学生 ID 保存为 action.student.因此,对于数组的所有元素,比较将返回 true.所以每次所有元素都将包含在新状态中.所以试着像这样改变你上面的代码

case DELETE_STUDENT://console.log("action.student", action.student);//console.log("state", state);newState = state.students.filter(function(student) {返回 student.id !== action.student;});返回新状态;

I am new to React and Redux and just getting comfortable with the idea of managing state and React in general. I preface that as I may need the possible solution to be in the context of what I have—essentially so I will better understand it. :)

That being said this is my problem:

I have created a list/form component in React but having trouble with two glaring problems.

While the item gets removed for the database, it is only reflected in the view upon a refresh You may have noticed the list # or the ID column doesn't subtract when items are removed from the list. I am using PostgreSQL on the backend and Sequelize as my Object/Relational Mapper and React for my views/components.

I have provided a gif so you all can see what I mean.

Thanks in advance!

This is my code:

React: Student.js

import React, { Component } from "react";
import store from "../store";
import { deleteStudent } from "../reducers";

export default class Students extends Component {
  constructor(props) {
    super(props);
    this.state = store.getState();
    this.deleteStudent = this.deleteStudent.bind(this);
  }

  componentDidMount() {
    this.unsubscribe = store.subscribe(() => {
      this.setState(store.getState());
    });
  }

  componentWillUnmount() {
    this.unsubscribe();
  }

  deleteStudent(index) {
    store.dispatch(deleteStudent(index));
    this.setState(store.getState());
  }

  render() {
    var students = this.props.students;
    return (
      <div className="container">
        <div className="sixteen columns">
          <h1 className="remove-bottom">Students</h1>
          <h5>List of current students and their campus</h5>
          <hr />
        </div>
        <div className="sixteen columns">
          <div className="example">
            <div>
              <table className="u-full-width">
                <thead>
                  <tr>
                    <th>#</th>
                    <th>Name</th>
                    <th>Email</th>
                    <th>Campus</th>
                  </tr>
                </thead>
                <tbody>
                  {students.map(function(student, index) {
                    return (
                      <tr key={index}>
                        <td>
                          {student.id}
                        </td>
                        <td>
                          {student.name}
                        </td>
                        <td>
                          {student.email}
                        </td>
                        <td>
                          {student.campus}
                        </td>
                        <td>
                          <a
                            className="button button-icon"
                            onClick={() => {
                              console.log(student.id);
                              this.deleteStudent(student.id);
                            }}
                            key={index}
                          >
                            <i className="fa fa-remove" />
                          </a>
                        </td>
                      </tr>
                    );
                  }, this)}
                </tbody>
              </table>
            </div>
          </div>
        </div>
      </div>
    );
  }
}

StudentForm.js

import React, { Component } from "react";
import store from "../store";
import { postStudent } from "../reducers";

const blankFormState = {
  name: "",
  email: "",
  campus: ""
};

export default class StudentForm extends Component {
  constructor(props) {
    super(props);
    this.state = blankFormState;
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    const target = event.target;
    this.setState({
      [target.name]: target.value
    });
  }

  handleSubmit(event) {
    event.preventDefault();
    store.dispatch(postStudent(this.state));
    this.setState(blankFormState);
  }

  render() {
    return (
      <div className="container">
        <div className="row">
          <div className="twelve columns">
            <form onSubmit={this.handleSubmit}>
              <div className="row">
                <div className="four columns">
                  <label>Name</label>
                  <input
                    className="u-full-width"
                    type="text"
                    name="name"
                    value={this.state.name}
                    onChange={this.handleChange}
                  />
                </div>
                <div className="four columns">
                  <label>Email</label>
                  <input
                    className="u-full-width"
                    type="text"
                    name="email"
                    value={this.state.email}
                    onChange={this.handleChange}
                  />
                </div>
                <div className="four columns">
                  <label>Campus</label>
                  <input
                    className="u-full-width"
                    type="text"
                    name="campus"
                    value={this.state.campus}
                    onChange={this.handleChange}
                  />
                </div>
              </div>
              <input className="button-primary" type="submit" />
            </form>
          </div>
        </div>
      </div>
    );
  }
}

My reducer.js

import { combineReducers } from "redux";
import axios from "axios";

const logError = console.error.bind(console);

// INITIAL STATE

const initialState = {
  students: [],
  campuses: []
};

//ACTION CREATORS

const UPDATE_NAME = "UPDATE_NAME";
const ADD_STUDENT = "ADD_STUDENT";
const DELETE_STUDENT = "DELETE_STUDENT";
const GET_STUDENTS = "GET_STUDENTS";
const UPDATE_CAMPUS = "UPDATE_CAMPUS";
const GET_CAMPUS = "GET_CAMPUS";
const GET_CAMPUSES = "GET_CAMPUSES";

// ACTION CREATORS

export function updateName(name) {
  const action = {
    type: UPDATE_NAME,
    name
  };
  return action;
}

export function addStudent(student) {
  return {
    type: ADD_STUDENT,
    student
  };
}

export function scrubStudent(student) {
  return {
    type: DELETE_STUDENT,
    student
  };
}

export function getStudents(students) {
  const action = {
    type: GET_STUDENTS,
    students
  };
  return action;
}

export function updateCampus(campus) {
  const action = {
    type: UPDATE_CAMPUS,
    campus
  };
  return action;
}

export function getCampus(campus) {
  const action = {
    type: GET_CAMPUS,
    campus
  };
  return action;
}

export function getCampuses(campuses) {
  const action = {
    type: GET_CAMPUSES,
    campuses
  };
  return action;
}

//THUNK CREATORS

export function fetchStudents() {
  return function thunk(dispatch) {
    return axios
      .get("/api/students")
      .then(function(res) {
        return res.data;
      })
      .then(students => {
        dispatch(getStudents(students));
      })
      .catch(logError);
  };
}

export function postStudent(student) {
  return function thunk(dispatch) {
    return axios
      .post("/api/students", student)
      .then(function(res) {
        return res.data;
      })
      .then(function(newStudent) {
        return dispatch(addStudent(newStudent));
      })
      .catch(logError);
  };
}

export function deleteStudent(id) {
  // console.log("student", student);
  return function thunk(dispatch) {
    return axios
      .delete("/api/students" + "/" + id)
      .then(function(id) {
        return dispatch(scrubStudent(id));
      })
      .catch(function(err) {
        return console.error("Removing student: " + id + " unsuccessful", err);
      });
  };
}

export function fetchCampuses() {
  return function thunk(dispatch) {
    return axios
      .get("/api/campuses")
      .then(function(res) {
        return res.data;
      })
      .then(function(campuses) {
        return dispatch(getCampuses(campuses));
      })
      .catch(logError);
  };
}

export function postCampus(student) {
  return function thunk(dispatch) {
    return axios
      .post("/api/campuses", campus)
      .then(function(res) {
        return res.data;
      })
      .then(function(newCampus) {
        return dispatch(getCampus(newCampus));
      })
      .catch(logError);
  };
}

// REDUCER

const rootReducer = function(state = initialState, action) {
  var newState = Object.assign({}, state);

  switch (action.type) {
    case GET_STUDENTS:
      newState.students = state.students.concat(action.students);
      return newState;

    case ADD_STUDENT:
      newState.students = state.students.concat([action.student]);
      return newState;

    case DELETE_STUDENT:
      // console.log("action.student", action.student);
      // console.log("state", state);
      newState = state.students.filter(function(student) {
        return student.id !== action.id;
      });
      return newState;

    case GET_CAMPUSES:
      newState.campuses = state.campuses.concat(action.campuses);
      return newState;

    case GET_CAMPUS:
      newState.campuses = state.campuses.concat([action.campus]);
      return newState;

    default:
      return state;
  }
};

export default rootReducer;

This is how I mount the Students and StudentForm

import React, { Component } from "react";
import Students from "./Students";
import StudentForm from "./StudentForm";
import store from "../store";

import { fetchStudents } from "../reducers";

export default class StudentContainer extends Component {
  constructor(props) {
    super(props);
    this.state = store.getState();
  }

  componentDidMount() {
    store.dispatch(fetchStudents());
    this.unsubscribe = store.subscribe(() => this.setState(store.getState()));
  }

  componentWillUnmount() {
    this.unsubscribe();
  }

  render() {
    return (
      <div>
        <Students students={this.state.students} />
        <StudentForm />
      </div>
    );
  }
}

My store.js

import { createStore, applyMiddleware } from "redux";
import rootReducer from "./reducers";
import createLogger from "redux-logger"; // https://github.com/evgenyrodionov/redux-logger
import thunkMiddleware from "redux-thunk"; // https://github.com/gaearon/redux-thunk

export default createStore(
  rootReducer,
  applyMiddleware(thunkMiddleware, createLogger())
);

解决方案

After deleting the student you are dispatching the action and you are passing the action creator scrubStudent to dispatch. You are passing id of the deleted student in that action creator. Now the way you have defined your action creator is like this

export function scrubStudent(student) {
  return {
    type: DELETE_STUDENT,
    student
  };
}

So the returned value of this function will be an object something like this

scrubStudent(5) // returns {type: "DELETE_STUDENT", student: 5}

But in your reducer you are comparing the ids like this

case DELETE_STUDENT:
      // console.log("action.student", action.student);
      // console.log("state", state);
      newState = state.students.filter(function(student) {
        return student.id !== action.id;
      });
      return newState;

In the above code action.id is undefined. Instead student id is saved in as action.student. So the comparison will return true for all the elements of array. So everytime all the elements will be included in the new state. So try to change your above code like this

case DELETE_STUDENT:
      // console.log("action.student", action.student);
      // console.log("state", state);
      newState = state.students.filter(function(student) {
        return student.id !== action.student;
      });
      return newState;

这篇关于在使用 Redux 从我的商店/状态中删除项目后,如何让我的视图更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆