视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
使用store来优化React组件的方法
2020-11-27 22:27:17 责编:小采
文档

在使用 React 编写组件的时候,我们常常会碰到两个不同的组件之间需要共享状态情况,而通常的做法就是提升状态到父组件。但是这样做会有一个问题,就是尽管只有两个组件需要这个状态,但是因为把状态提到了父组件,那么在状态变化的时候,父组件以及其下面的所有子组件都会重新 render,如果你的父组件比较复杂,包含了其他很多子组件的话,就有可能引起性能问题。

Redux 通过把状态放在全局的 store 里,然后组件去订阅各自需要的状态,当状态发生变化的时候,只有那些订阅的状态发生变化的组件才重新 render,这样就避免了上面说的提升状态所带来的副作用。但是,当我们在写一个 React 组件库的时候,redux 加 react-redux 的组合可能就有点太重了。所以我们可以自己写一个简单的 store,来实现类似 Redux 的订阅模式。

参考 Redux 的实现来写一个简版的 createStore:

function createStore(initialState) {
 let state = initialState;
 const listeners = [];

 function setState(partial) {
 state = {
 ...state,
 ...partial,
 };
 for (let i = 0; i < listeners.length; i++) {
 listeners[i]();
 }
 }

 function getState() {
 return state;
 }

 function subscribe(listener) {
 listeners.push(listener);

 return function unsubscribe() {
 const index = listeners.indexOf(listener);
 listeners.splice(index, 1);
 };
 }

 return {
 setState,
 getState,
 subscribe,
 };
}

我们的 createStore 非常简单,算上空行也只有 33 行,总共暴露了 3 个方法,没有 Redux 里的 dispatch 和 reducer,直接通过 setState 方法改变状态。下面我们来用它一个计数器的例子。

class Counter extends React.Component {
 constructor(props) {
 super(props);

 // 初始化 store
 this.store = createStore({
 count: 0,
 });
 }

 render() {
 return (
 <div>
 <Buttons store={store} />
 <Result store={store} />
 </div>
 )
 }
}

class Buttons extends React.Component {
 handleClick = (step) => () => {
 const { store } = this.props;
 const { count } = store.getState();
 store.setState({ count: count + step });
 }

 render() {
 return (
 <div>
 <button onClick={this.handleClick(1)}>+</button>
 <button onClick={this.handleClick(1)}>-</button>
 </div>
 );
 }
}

class Result extends React.Component {
 constructor(props) {
 super(props);

 this.state = {
 count: props.store.getState().count,
 };
 }

 componentDidMount() {
 this.props.store.subscribe(() => {
 const { count } = this.props.store.getState();
 if (count !== this.state.count) {
 this.setState({ count });
 }
 });
 }

 render() {
 return (
 <div>{this.state.count}</div>
 );
 };
}

例子中 Buttons 里通过 store.setState 来改变 store 中的状态,并不会引起整个 Counter 的重新 render,但是因为 Result 中订阅了 store 的变化,所以当 count 有变化的时候就可以通过改变自己组件内的状态来重新 render,这样就巧妙地避免了不必须要的 render。

最后,上面的 createStore 虽然只有几十行代码,我还是把它写成了一个叫 mini-store 库放在 GitHub 上,并且提供了类似 Redux 的 Provider 和 connect 方法,总共加起来也就 100 多行代码。如果你也在写 React 组件库,需要管理一个复杂组件的状态,不妨试试这个优化方式。

下载本文
显示全文
专题