我每天都有一张员工表及其每月工作时间。在这里,我们可以批量更新所有员工小时数。
当月的一个简单数学:30天x 50名员工将产生1500个安装的Redux表单字段。
每次挂载Field时,Redux Form都会调度一个操作以在Redux存储中注册Field。因此,调度了1500个事件。
使用Chrome-> Performance工具进行调试,我发现从安装到分发再到渲染Field的整个过程大约需要4秒钟:
以上性能得分基于我使用React,Redux,Redux Form,Reselect创建的以下简单工作示例:
/* ----------------- SAMPLE DATA GENERATION - START ----------------- */
const generateEmployees = length =>
Array.from({ length }, (v, k) => ({
id: k + 1,
name: `Emp ${k + 1}`,
hours: [8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8]
}))
const employees = generateEmployees(50)
/* ----------------- SAMPLE DATA GENERATION - END ------------------- */
/* --------------------- INITIALIZATION - START --------------------- */
const { reduxForm, Field, reducer: formReducer } = ReduxForm
const { createStore, combineReducers, applyMiddleware } = Redux
const { Provider, connect } = ReactRedux
const { createSelector } = Reselect
// Creating the Reducers
const employeesReducer = (state = employees) => state
const reducers = {
form: formReducer,
employees: employeesReducer
}
// Custom logger.
// The idea here is log all the dispatched action,
// in order to illustrate the problem with many registered fields better.
const logger = ({ getState }) => {
return next => action => {
console.log("Action: ", action)
return next(action)
}
}
const reducer = combineReducers(reducers)
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
applyMiddleware(logger)
)
/* --------------------- INITIALIZATION - END ----------------------- */
const renderEmployees = employees =>
employees.map(employee => {
return (
<tr key={employee.id}>
<td>{employee.id}</td>
<td>{employee.name}</td>
{employee.hours.map((hour, day) => (
<td key={day}>
<Field component="input" name={`${employee.id}_${day}`} />
</td>
))}
</tr>
)
})
const FormComponent = ({ employees, handleSubmit }) => {
return (
<form onSubmit={handleSubmit}>
<h2>Employees working hours for November (11.2018)</h2>
<p>
<button type="submit">Update all</button>
</p>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
{Array.from({ length: 30 }, (v, k) => (
<th key={k + 1}>{`${k + 1}.11`}</th>
))}
</tr>
</thead>
{renderEmployees(employees)}
</table>
</form>
)
}
const Form = reduxForm({
form: "workingHours",
onSubmit: submittedValues => {
console.log({ submittedValues })
}
})(FormComponent)
const getInitialValues = createSelector(
state => state.employees,
users =>
users.reduce((accumulator, employee) => {
employee.hours.forEach(
(hour, day) => (accumulator[`${employee.id}_${day}`] = hour)
)
return accumulator
}, {})
)
const mapStateToProps = state => ({
employees: state.employees,
initialValues: getInitialValues(state)
})
const App = connect(mapStateToProps)(Form)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
)
table {
border-collapse: collapse;
text-align: center;
}
table, th, td {
border: 1px solid black;
}
th {
height: 50px;
padding: 0 15px;
}
input {
width: 20px;
text-align: center;
}
<script src="https://unpkg.com/react@15.5.4/dist/react.js"></script>
<script src="https://unpkg.com/react-dom@15.5.4/dist/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.6.0/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/5.0.4/react-redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux-form/6.7.0/redux-form.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reselect/3.0.1/reselect.js"></script>
<div id="root">
<!-- This element's contents will be replaced with your component. -->
</div>
那么我是否在Redux Form上做效率低下或做错事还是一次渲染大量Field是瓶颈,我应该采取其他方法(分页,延迟加载等)? < / p>
答案 0 :(得分:1)
您尝试过https://www.npmjs.com/package/react-virtualized是在一个我捕获了数十个事件的项目中使用的。列表越来越多,这个组件帮助我渲染了所有这些。我不确定Redux Form的工作方式,但是如果它基于挂载,那么我想这是一个不错的选择。
答案 1 :(得分:1)
当陷入这种复杂性时,现有的开源表单库似乎无法很好地工作。我们通过构建自己的表单库实现了一些优化,但是您可能可以将其中的一个或多个想法与redux表单一起使用。
我们安装了许多表单和其他Redux附加组件(成千上万个),它们在安装时都会进行验证。我们的架构要求立即安装所有组件。每个组件在安装时可能需要多次更新状态。这不是很快,但是这些是我们用来使此 似乎 快速实现的优化:
每个组件的安装方式都可以检查它是否真的需要立即分发某些东西,或者可以等到以后。如果状态值错误或当前值无效-仍将导致调度,否则它将推迟其他更新以供以后使用(例如当用户将其聚焦时)。
实现了一个可在单个调度中处理多个动作的批处理减速器。这意味着,如果某个组件确实需要在挂载上执行多个操作,则最多只会发送1个调度消息,而不是每个操作仅发送一个调度消息。
使用redux批处理中间件消除反应重新渲染。这意味着当发生大的redux更新时,将触发react减少渲染频率。我们在redux更新的前沿和下降沿触发侦听器,这意味着react将在第一次调度时更新,此后每500-1000ms更新一次,直到不再发生更新。对于我们来说,这可以改善很多性能,但是取决于控件的工作方式,这也可能会使它看起来有些滞后(有关解决方案,请参阅下一项)
本地控制状态。每个表单控件都有一个本地状态,并立即响应用户交互,当用户从控件中跳出时,它们会延迟更新redux状态。这使事情看起来很快,并导致更少的Redux更新。 (redux更新非常昂贵!)
我不确定以上哪一项对您有帮助,甚至不确定以上内容是否明智或推荐使用-但对我们来说效果很好,我希望可以给您一些想法。
此外,如果您可以减少安装更少的组件(分页等),那么绝对应该这样做。我们受到一些早期架构选择的限制,被迫坚持,这就是我们想出的。