使用redux在秒表上计时并将其放入数据库



有人建议我使用redux将时间放入数据库,但我遇到了问题。我在index.js中有一个秒表,用户可以启动和停止。之后,我有一个按钮,允许他们将时间放入数据库。从我的节点文件中,我得到了UnhandledPromiseRejectionWarning: error: null value in column "time" violates not-null constraint。我想知道我得到它是因为我在索引文件的末尾有difference = 0,它没有检索到difference = this.state.endTime - this.state.startTime;,还是我没有看到另一个问题。

index.js

export let difference = 0;
export default class Home extends React.Component {
constructor(props);
super(props);
this.state = {
startTime: 0,
endTime: 0,
}
}
handleStart = () => {
this.setState({
startTime: Date.now()
})
}
handleStop = () => {
this.setState({
endTime: Date.now()
})
}
render() {
difference = this.state.endTime - this.state.startTime;
return (
<App />
<reducer />
);
}
}

reducer.js

import * as actionTypes from "./actions.js";
import difference from "../pages/index.js";
const initialState = {
count: 0
}
const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.NUMBER:
return { state, count: difference };
default: return state;
}
};
export default reducer;

store.js

import { createStore } from 'redux';
import reducer from './reducer';
const store = createStore(reducer);



export default store;

actions.js

export const NUMBER = 'NUMBER';

App.js

import React from 'react';
import { addName } from "./util";
function App() {
const [name, setName] = React.useState("")
function handleUpdate(evt) {
setName(evt.target.value);
}
async function handleAddName(evt) {
await addName(name);
}
return <div>
<p><input type='text' value={name} onChange={handleUpdate} /></p>
<button className='button-style' onClick={handleAddName}>Add Name</button>
</div>
}
export default App;

util.js

import "isomorphic-fetch"
import difference from "../pages/index.js";
export function addName(name) {
return fetch('http://localhost:3001/addtime', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, time: difference }) 
})
}

节点服务器.js

app.post("/addtime", cors(), async (req,res) => {
const name = req.body.name;
const time = req.body.time;
const timeStamp = dateFormat(time, dateFormat.masks.isoDateTime);

const template = 'INSERT INTO times (name,time) VALUES ($1,$2)';
const response = await pool.query(template, [name,time]);

res.json({name: name, time: time});
});

状态管理

在React中,我们通过状态管理不断变化的价值观。有一种访问值的方法,也有一种更新值的方法。当值发生变化时,React知道要重新渲染组件。

此状态可以存储在应用程序的组件中。当你写const [name, setName] = React.useState("")时,name就是你的"吸气剂";而CCD_ 6是您的";setter";。

该状态也可以存储在上下文Provider组件内,该组件是当前组件的父组件。该Provider的任何子级都可以使用useContext钩子来访问存储的值。

React Redux包使用这些React上下文使Redux状态全局可用。您将整个App封装在ReduxProvider组件中以启用访问。

使用Redux,您的";getters";是选择器函数,您可以通过useSelector调用它,而您的";setters";是通过dispatch函数传递的操作。React实际上通过useReducer钩子在本地组件级别(没有Redux)支持这种状态管理语法。

您不需要使用上下文来在组件之间传递状态。您还可以传递值和";setters";作为从父组件到子组件的道具。React建议采用这种方法,并有一节介绍提升状态。

这只是一些概念的简要概述。你在这里遇到的问题是由于国家管理。这里有很多可能的方法来处理你的状态。在我看来,Redux和Contexts都是不必要的过度使用。当然,如果你想的话,你可以使用它们,但你需要正确设置它们,而你还没有这样做。

错误

export let difference = 0;

存在于文件顶层、组件外部的变量应仅为不可变常量。当您的值发生更改时,它需要成为应用程序状态的一部分。

当你有一个";有状态的";像difference这样的值,不能只使用存在于组件之外的变量。

render() {
difference = this.state.endTime - this.state.startTime;
...

这不是更新值的方式。我们不想只是设置一个常数。我们也不想在每次渲染时触发有状态值的更改,因为这会产生无限的重新渲染情况。

handleStart & handleStop

函数本身很好,但它们从不在任何地方调用。因此difference将始终是0

<reducer />

减速器不是一个组件,因此不能这样调用它。这是一个纯粹的函数。为了在JSX代码中引入redux,您可以使用reducer创建一个store变量,并将该存储传递给react reduxProvider组件。

const reducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.NUMBER:
return { state, count: difference };
default: return state;
}
};

您希望将difference包含为action的属性,而不是访问外部变量。标准约定是向操作添加payload属性。有效载荷可以是差number本身或者具有属性differenceobject。那种设计选择由你决定。一个动作可能看起来像{ type: actionTypes.NUMBER, payload: 0.065 }{ type: actionTypes.NUMBER, payload: { difference: 0.065 } }

您的state是一个属性为count的对象。您的reducer应该返回下一个状态,该状态也应该是具有属性count的对象。这将不会返回正确的状态形状:return { state, count: difference };

通常使用排列运算符...来复制状态的所有其他属性,并只更新一个(或几个),如下所示:return { ...state, count: difference }。但是,您的state没有任何其他属性,因此它与return { count: difference };相同。由于您只存储一个数字,因此没有复制或保留以前状态的值。(这是我认为Redux在这里没有帮助或必要的很大一部分原因。)

后端可能也有一些问题,但前端存在严重问题,我认为这是您的主要问题。

结构化组件

想想你的应用程序需要做什么。它会响应什么操作?它需要知道什么信息?

根据您的描述,它需要:

  • 单击按钮时启动计时器
  • 单击按钮时停止计时器
  • 存储最新的计时器值
  • 允许用户输入并更新名称
  • 存储该名称
  • 如果存在存储的差异和名称,则在单击按钮时POST到后端

接下来,您将其分解为多个组件。将每个有状态值存储在树中需要访问它的最高组件上,并将值和回调作为道具向下传递。startTimeendTime仅由定时器本身使用,但定时器需要在停止后设置difference。CCD_ 39将被存储在更高的位置,因为POST请求也需要它。

想想在任何给定的时间都有哪些按钮和操作可用,以及使用什么条件来确定这一点。例如,在启动计时器之前,您不应该停止计时器。因此,我们将查看startTime是否大于0

最小前端示例

import * as React from "react";
const Timer = ({ setDifference }) => {
const [startTime, setStartTime] = React.useState(0);
const handleStart = () => {
setStartTime(Date.now());
};
const handleStop = () => {
const difference = Date.now() - startTime;
setDifference(difference);
};
return (
<div>
<button onClick={handleStart}>
Start
</button>
<button disabled={startTime === 0} onClick={handleStop}>
Stop
</button>
</div>
);
};
const Submission = ({ time }) => {
const [name, setName] = React.useState("");
function handleUpdate(evt) {
setName(evt.target.value);
}
async function handleAddName(evt) {
return await fetch("http://localhost:3001/addtime", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name, time })
});
}
return (
<div>
<div>Time: {time}</div>
<input type="text" value={name} onChange={handleUpdate} />
<button onClick={handleAddName} disabled={name.length === 0}>
Add Name
</button>
</div>
);
};
const App = () => {
const [time, setTime] = React.useState(0);
return (
<div>
<Timer setDifference={setTime} />
{time > 0 && <Submission time={time} />}
</div>
);
};
export default App;

CodeSandbox链接

您可能没有将任何值保存到DOB列,您需要允许NULL值。您应该更改为DOB=型号。DateField(auto_now=False,null=True)您还应该确保表中的DOB列允许为null。

最新更新