简体   繁体   English

如何在React组件/视图中同步DOM和数据库

[英]How to sync the DOM and Database in a React Component/View

I have created a list component in React but having trouble with two glaring problems. 我已经在React中创建了一个列表组件,但是遇到了两个明显的问题。

  1. While the item gets removed (from the database as well )it is only reflected upon a refresh 当项目被删除(以及从数据库中删除)时,它仅在刷新时反映出来
  2. You may have noticed the list # or the ID column doesn't subtract when items are removed from the list. 您可能已经注意到列表#或从列表中删除项目时ID列未减去。

I am using PostgreSQL on the backend and Sequelize as my Object/Relational Mapper and React for my views/components. 我在后端使用PostgreSQL,并将Sequelize用作我的视图/组件的对象/关系映射器和React。

I have provided a gif so you all can see what I mean. 我提供了一个gif,以便大家都能理解我的意思。

Thanks in advance! 提前致谢!

This is my code: 这是我的代码:

React: Student.js 反应: 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.state = 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>
    );
  }
}

My Reducer: 我的减速机:

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);
      state.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;

And this is my Student Model: 这是我的学生模型:

'use strict';
var Sequelize = require('sequelize')
var db = require('../index.js')

//hasOne, hasMany, belongsTo, belongsToMany Sequelize methods

module.exports = db.define('student', {
  name: {
    type: Sequelize.STRING,
    allowNull: false,

  },

  email: {
    type: Sequelize.STRING,
    allowNull: false,
    unique: true,
    validate: {
      isEmail: true
    }
  },

  campus: {
    type: Sequelize.STRING,
    allowNull: false,

  }
})

在此处输入图片说明

I want to answer this but there is a bit to much that your missing in this. 我想回答这个问题,但是您在此方面有很多不足。 I think you need to place it in a GIT repo and get more help that way. 我认为您需要将其放入GIT仓库中,并以此方式获得更多帮助。

first thing you need to do is to create a proper store configuration/build. 您需要做的第一件事是创建正确的存储配置/构建。 Something akin to the below... 类似于以下内容...

import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import createLogger from 'redux-logger'
import baseMiddleware from 'redux/middleware/'
import reducer from 'redux/modules/reducer'
import { routerMiddleware } from 'react-router-redux'
import { persistState } from 'redux-devtools';

export default function configureStore(initialState, history) {
    const middleware = baseMiddleware.with(thunk, createLogger(), routerMiddleware(history))

    const store = createStore(
        reducer,
        initialState,
        compose(
            applyMiddleware(...middleware),
            window.devToolsExtension ? window.devToolsExtension() : f => f,
            persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/))
        )
    )

    if (module.hot) {
        // Enable Webpack hot module replacement for reducers
        module.hot.accept('redux/modules/reducer', () => {
            const nextRootReducer = require('redux/modules/reducer').default
            store.replaceReducer(nextRootReducer)
        })
    }

    return store
}

This references two main files. 这引用了两个主要文件。 One for my reducers and the other for my middleware. 一个用于我的减速器,另一个用于我的中间件。 This is important because it fits into the S of SOLID 这很重要,因为它适合SOLID的S

This is my middleware... 这是我的中间件...

import api from './api'
import authenticationMiddleware from './authenticationMiddleware'
import analyticsMiddleware from './analyticsMiddleware'

const middleware = [ api, analyticsMiddleware, authenticationMiddleware ]

export default {
    with(){
        for(let i = 0; i < arguments.length; i++){
            middleware[middleware.length] = arguments[i];
        }

        return middleware
    },
    ...middleware
}

and reducers 和减速器

import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';

import {reducer as form} from 'redux-form';

import authentication from './authentication'

const reducer = combineReducers({
    router: routerReducer,
    authentication
})

export default reducer

You can then pass in your store into your root component and use Provider higher order component to then add the Redux context to the object graph 然后,您可以将商店传递到根组件中,并使用Provider高阶组件将Redux上下文添加到对象图中

import React, { Component, PropTypes } from 'react'
import { Provider } from 'react-redux'
import routes from '../routes'
import { Router } from 'react-router'

export default class Root extends Component {
  render() {
    const { store, history } = this.props
    return (
      <Provider store={store}>
        <div>
          <Router history={history} routes={routes} />
        </div>
      </Provider>
    )
  }
}

Root.propTypes = {
  store: PropTypes.object.isRequired,
  history: PropTypes.object.isRequired
}

Now how you pass in the store to the Root component is more on you and wether you choose to have history, i'll leave that last part up to you. 现在,如何将存储传递到Root组件的方法更多,并且您选择拥有历史记录,我将最后一部分交给您。

You will also need to start using the Connect function on your components to create containers 您还需要开始在组件上使用Connect函数来创建容器

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM