我正在开发一个应用程序,用户可以在其中更改输入值,并应根据这些值生成一些计算出的值。在此演示中,我仅需要填写三个输入(它们的值存储在数组中的单独文件data.js中),并且只有一个输入被计算为前三个输入的平均值。
项目结构如下:
src / index.js
import React from "react";
import ReactDOM from "react-dom";
import Block1 from "../components/Block1";
import getStore from "../store";
const store = getStore();
const rootEl = document.getElementById("root");
const render = () => {
ReactDOM.render(<Block1 />, rootEl);
};
render();
store.subscribe(() => {
render();
console.log("not working");
});
components / Block1.js
import React, { Component } from "react";
import getStore from "../store";
const store = getStore();
function Item(props) {
return (
<tr key={props.id}>
<td>{props.rn}</td>
<td>{props.name}</td>
{props.not !== "" ? (
<td>
<img src={"img/" + props.not} />
</td>
) : (
<td />
)}
{props.isInput ? (
<td>
<input type="text" value={props.val} onChange={props.onChange} />
</td>
) : (
<td>{props.val}</td>
)}
<td>{props.um}</td>
</tr>
);
}
export default class Block1 extends Component {
renderItem(d) {
return (
<Item
key={d.id}
id={d.id}
rn={d.rn}
name={d.name}
not={d.not}
val={d.val}
um={d.um}
isInput={d.isInput}
onChange={e =>
store.dispatch({
type: "changeValue",
payload: {
id: d.id,
val: e.target.value
}
})
}
/>
);
}
render() {
return (
<div>
<h2>Block 1. Initial data for calculation</h2>
<table className="table">
<thead>
<tr>
<th>№</th>
<th>Name</th>
<th>Notation</th>
<th>Value</th>
<th>Unit of measure</th>
</tr>
</thead>
<tbody>{store.getState().map(d => this.renderItem(d))}</tbody>
</table>
</div>
);
}
}
reducers / index.js
import getData from "../data.js";
const data = getData();
const Parser = require("expr-eval").Parser;
export default (state = data, action) => {
if (action.type === "changeValue") {
let changedId = action.payload.id;
// change value in input
let data = state.slice();
let el = data.find(d => d.id === changedId);
el.val = parseFloat(action.payload.val);
// find all id to re-calculate
const toRecalculate = getAllDependentDataId(changedId);
// recalculation
toRecalculate.forEach(r => {
// ... here some calculation logic which gives the result
let el = data.find(d => d.id === r);
el.val = Math.round((result + 0.00001) * 100) / 100;
return data;
});
}
return state;
};
store / index.js
import reducer from "../reducers";
import { createStore } from "redux";
import getData from "../data.js";
export default function getStore() {
const store = createStore(reducer, getData());
return store;
}
完整的示例是here
问题在于,由于某种原因,UI中不会更新新值。 store.subscribe(...)内部的代码无法运行。但是,状态正在改变-数组中的值已更新-我使用console.log检查了此结果...
请帮助我找出问题所在。
答案 0 :(得分:3)
您缺少动作/动作创建者,无法从redux连接connect,provider,mapStateToProps和mapDispatchToProps。如果您仅具有这4个值,那么我就不会使用redux,而只会使用本地状态作为受控组件。
如果您尝试使用redux,那么我将从这里开始。
这是一个简单的例子,可以解决redux的平均问题: https://codesandbox.io/s/422q1znj4w
components / Block1.js
import React, { Component } from "react";
import { connect } from "react-redux";
import { changeValue, loadData } from "../actions/action";
import getData from "../data";
function Item(props) {
return (
<tr key={props.id}>
<td>{props.rn}</td>
<td>{props.name}</td>
{props.not !== "" ? (
<td>
<img src={"img/" + props.not} />
</td>
) : (
<td />
)}
{props.isInput ? (
<td>
<input type="text" value={props.val} onChange={props.onChange} />
</td>
) : (
<td>{props.val}</td>
)}
<td>{props.um}</td>
</tr>
);
}
class Block1 extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.props.loadData(getData());
}
renderItem(d) {
console.log(d.val);
return (
<Item
key={d.id}
id={d.id}
rn={d.rn}
name={d.name}
not={d.not}
val={d.val}
um={d.um}
isInput={d.isInput}
onChange={e =>
this.props.changeValue({ id: d.id, value: e.target.value })
}
/>
);
}
render() {
return (
<div>
<h2>Block 1. Initial data for calculation</h2>
<table className="table">
<thead>
<tr>
<th>№</th>
<th>Name</th>
<th>Notation</th>
<th>Value</th>
<th>Unit of measure</th>
</tr>
</thead>
<tbody>{this.props.data.map(d => this.renderItem(d))}</tbody>
</table>
</div>
);
}
}
const mapStateToProps = state => {
return {
data: state.data
};
};
const mapDispatchToProps = dispatch => {
return {
changeValue: item => dispatch(changeValue(item)),
loadData: data => dispatch(loadData(data))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Block1);
actions / action.js
export function changeValue(data) {
return {
type: "changeValue",
data
};
}
export function loadData(data) {
return {
type: "loadData",
data
};
}
reducers / data.js
export function data(state = [], action) {
if (action.type === "loadData") {
return action.data;
} else {
if (action.type === "changeValue") {
let newState = [...state];
newState[action.data.id - 1].val = action.data.value;
let counter = 0;
let sum = 0;
for (let input of newState) {
if (input.isInput) {
sum += parseInt(input.val);
counter++;
}
}
newState[state.length - 1].val = sum / counter;
return newState;
}
}
return state;
}
reducers / rootReducer.js
import { combineReducers } from "redux";
import { data } from "./data";
const rootReducer = combineReducers({
data
});
export default rootReducer;
src / index.js
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import configureStore from "../store";
import Block1 from "../components/Block1";
const store = configureStore();
const rootEl = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<Block1 />
</Provider>,
rootEl
);
store / index.js
import { createStore } from "redux";
import rootReducer from "../reducers/rootReducer";
export default function configureStore() {
return createStore(
rootReducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
}
答案 1 :(得分:0)
就在每个dispatch()调用之前对订阅进行快照。如果在调用侦听器时进行订阅或取消订阅,则这不会对当前正在进行的dispatch()产生任何影响。但是,下一个dispatch()调用(无论是否嵌套)都将使用订阅列表的最新快照。
https://redux.js.org/api/store#subscribelistener
因此,一旦在创建商店后立即创建订阅(OR) 在调度动作调用之前。
const store = createStore(reducer);
//SUBSCRIBE the state change
store.subscribe(() => {
console.log(store.getState());
});
//ACTION
store.dispatch({ type: "ADD", val: 1 });