优化 Redux Store
介绍
Redux 提供了一种具有良好泛化的模式。这种模式有时会导致将复杂的场景视为简单的情况。
好消息是,优化 redux 存储与优化任何其他软件并没有太大区别 - 它总是回归到数据结构和算法。永远如此。
别担心,没什么可怕的。我们将在本指南中使用两种技术:
数据规范化(我们将使用哈希表来优化查找)
记忆化(避免不必要的重新计算)
示例应用程序
我们将在这里使用一个与您可能已经使用的应用程序没有太大区别的应用程序。我们将维护一个值列表并显示其详细信息。
该应用程序将:
保留对象列表(格式{ id, number })并将其显示为无序列表(<ul>)。
以fib(n) = 第 n 个斐波那契数(即斐波那契数列中的第 n个元素)的格式显示每个对象。
回顾一下,斐波那契数列如下所示:
0
每个数字都等于前两个数字的总和 - 除了0和1,它们等于它们自己。
我们将在此示例中使用的算法如下:
const fibonacci = (n) => {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
};
这种算法效率极低,即使数字很小也会导致堆栈溢出。这就是为什么它非常适合这个例子——我们想在这里强制一个糟糕的性能并展示如何改进它。
如果您好奇的话,递归算法的时间复杂度为O(2^n)。您可以非常轻松地实现自下而上的算法,避免递归并将复杂度降低到O(n),但这不是本指南的重点 - 让我们担心 Redux 吧!
代码
以下是该应用程序的代码:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import rootReducer from './reducers';
import './index.css';
import App from './App';
const store = createStore(rootReducer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'),
);
Reducers.js
import { combineReducers } from 'redux';
import fibonacciReducer from './FibonacciList/ducks';
const rootReducer = combineReducers({
fibonacci: fibonacciReducer,
});
export default rootReducer;
App.js
import React, { Component } from 'react';
import FibonacciList from './FibonacciList';
import './App.css';
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<FibonacciList />
</header>
</div>
);
}
}
export default App;
FibonacciList/index.js
import React from 'react';
import AddNumber from './addNumber';
import List from './list';
const FibonacciList = () => (
<>
<AddNumber />
<List />
</>
);
export default FibonacciList;
FibonacciList/addNumber.js
import React, { useState } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { addNumber } from './ducks';
const AddNumber = React.memo(({ addNumber }) => {
const [newNumber, setNewNumber] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (!newNumber) return;
addNumber(newNumber);
setNewNumber('');
};
const handleChange = (e) => setNewNumber(Number(e.target.value) || '');
return (
<form onSubmit={handleSubmit}>
<input type="number" onChange={handleChange} value={newNumber} />
<input type="submit" value="Create" />
</form>
);
});
const mapDispatchToProps = dispatch => bindActionCreators({ addNumber }, dispatch);
export default connect(null, mapDispatchToProps)(AddNumber);
FibonacciList/list.js
import React from 'react';
import { connect } from 'react-redux';
import Details from './details';
import { selectNumbers } from './ducks';
const List = React.memo(({ numbers }) => (
<ul>
{numbers.map(number => <Details key={number.id} numberId={number.id} />)}
</ul>
));
const mapStateToProps = (state) => ({
numbers: selectNumbers(state),
});
export default connect(mapStateToProps)(List);
FibonacciList/details.js
import React from 'react';
import { connect } from 'react-redux';
import { selectNumberDetails } from './ducks';
const Details = ({ details }) => (
<li>{`fib(${details.number}) = ${details.fibonacci}`}</li>
);
const mapStateToProps = (state, ownProps) => ({
details: selectNumberDetails(state, { id: ownProps.numberId }),
});
export default connect(mapStateToProps)(Details);
FibonacciList/ducks.js
// Actions
const ADD_NUMBER = 'ADD_NUMBER';
// Action Creators
export const addNumber = number => ({
type: ADD_NUMBER,
number,
});
// Reducer
const initialState = {
values: [...new Array(30)].map((_, i) => ({ id: String(i), number: i + 1 })),
};
export default function fibonacciListReducer(state = initialState, action) {
switch (action.type) {
case ADD_NUMBER:
return {
...state,
values: [
{ id: String(state.values.length), number: action.number },
...state.values,
],
};
default:
return state;
}
};
// Selectors
const fibonacci = (n) => {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
};
export const selectNumbers = state => state.fibonacci.values;
export const selectNumberDetails = (state, { id }) => {
const number = state.fibonacci.values.find(n => n.id === id);
return { ...number, fibonacci: fibonacci(number.number) };
}
这个应用程序存在许多问题,并且有多种改进方法,但让我们专注于增强redux 存储本身及其选择器。
改进查找(针对值数组)
过滤所花费的时间不是这个特定应用程序中的主要问题(斐波那契计算才是),但根据我的经验,这是现实世界中首先出现的问题之一。
每当您选择一个项目时,您的应用都会按顺序循环遍历数组中的每个元素,直到找到该对象。如果这个过程一遍又一遍地重复,或者如果你正在处理庞大的集合,那么这个时间很快就会增加,并成为一个令人头疼的问题。
处理这个问题的最佳方法是将你使用的结构转换为哈希映射。幸运的是,JavaScript 为我们简化了这一过程 - JS 中的对象已经是哈希映射了!
例子:
const notNormalized = [{
id: '1',
number: 3,
}, {
id: '2',
number: 5,
}];
// selecting from this array:
const item = notNormalized.find(item => item.id === '2'); // will run for all elements until reaching the element with id '2'
const normalized = {
'1': {
id: '1',
number: 3,
},
'2': {
id: '2',
number: 5,
},
};
// selecting from this hash map
const item2 = normalized['2']; // doesn't get worse as the array grows - time complexity of O(1)
要将数组转换为哈希映射 - 并假设数组中的每个元素都包含一个id - 让我们使用以下函数:
const
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~