React从零学起

前端之家收集整理的这篇文章主要介绍了React从零学起前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

原文请戳

初接触React,除了不习惯其组件化的设计原则外,往往它所‘依赖’的众多module也会让初学者感到困惑,使得不知从何学起。此文只是我对React的一些浅析,希望能帮助想要学习React的朋友入门。

1.React从来就是独立的

正如上面我提到的,React’依赖’了很多module,但是这种依赖并不是所谓的耦合依赖,只是为了更好的去实现React。换句话说,React只是一个View层面的框架,它可以和其他module自然的融合(更好的去实现)。

我们可以只利用React去实现官网上那个Counter的demo,这里我做了一个简易版。

index.html

  1. <!DOCTYPE html>@H_502_21@
  2. <html@H_502_21@>@H_502_21@
  3. <head@H_502_21@>@H_502_21@
  4. <title@H_502_21@>@H_502_21@Redux counter example</title@H_502_21@>@H_502_21@
  5. </head@H_502_21@>@H_502_21@
  6. <body@H_502_21@>@H_502_21@
  7. <div@H_502_21@ id@H_502_21@="root"@H_502_21@>@H_502_21@
  8. </div@H_502_21@>@H_502_21@
  9. <script@H_502_21@ src@H_502_21@="/static/bundle.js"@H_502_21@>@H_502_21@@H_502_21@</script@H_502_21@>@H_502_21@
  10. </body@H_502_21@>@H_502_21@
  11. </html@H_502_21@>@H_502_21@

页面只引了一个js文件,该文件为webpack打包而成,具体webpack打包原理不在这里赘述。

MyCounter.js

  1. var@H_502_21@ React = require('react'@H_502_21@);
  2. var@H_502_21@ Counter = React.createClass({
  3. getInitialState: function@H_502_21@()@H_502_21@ {@H_502_21@
  4. return@H_502_21@ {value: 0@H_502_21@};
  5. },plus: function@H_502_21@()@H_502_21@ {@H_502_21@
  6. this@H_502_21@.setState({
  7. value: ++this@H_502_21@.state.value
  8. });
  9. },minus: function@H_502_21@()@H_502_21@ {@H_502_21@
  10. this@H_502_21@.setState({
  11. value: --this@H_502_21@.state.value
  12. });
  13. },render: function@H_502_21@()@H_502_21@ {@H_502_21@
  14. return@H_502_21@ (
  15. <div>
  16. <button onClick={this@H_502_21@.plus}>+</button>
  17. <span>{this@H_502_21@.state.value}</span>
  18. <button onClick={this@H_502_21@.minus}>-</button>
  19. </div>
  20. );
  21. }
  22. });
  23. module.exports = Counter;

这是典型的React的Component,它的内部实现了计数的算法以及state的管理机制,这个Component的实例就是计数器,该计数器也很简单,可以实现增加和减少。

最后是 index.js

  1. var@H_502_21@ React = require@H_502_21@('react'@H_502_21@);
  2. var@H_502_21@ ReactDOM = require@H_502_21@('react-dom'@H_502_21@);
  3. var@H_502_21@ MyCounter = require@H_502_21@('./components/MyCounter'@H_502_21@);
  4. ReactDOM.render(
  5. <MyCounter />,document@H_502_21@.getElementById('root'@H_502_21@)
  6. );

到此,我们就利用React实现了一个小小的计数器,尽管它很简单,但是却未使用任何其他module,所以我在上面提到,React本身就是独立的。

所以,引用其他module,只是为了实现更复杂的React App,使得其更具有扩展性。

2.Container

那么问题来了,如果我还像要一个类似的Component,但是每次计数的时候不是加1或者减1,而是乘2或除2,那怎么做呢?

你可别告诉我重新一个类似上面MyCounter的Component,然后绑定不同的事件,那如果是这样的话React也太low了吧,这还叫什么组件化呢,组件化最基本的特点就是复用啊。

所以React期望我们这么做:

对于任何Compoent,尽量将其作为静态展示的Component,即其只负责展示UI,然后在它的外层嵌套一个Container, Container中定义了该Compoent所需要的参数以及方法,这样,当我们需要复用Component时,UI已经是现成的了,而Container中的逻辑部分也可以共享,换个“壳子”就是一个具有其他功能的Compoent了。

于是,分解如下:

MyCounterContainer.js

  1. var@H_502_21@ React = require('react'@H_502_21@);
  2. var@H_502_21@ Counter = require('../components/MyCounter'@H_502_21@);
  3. var@H_502_21@ CounterContainer = React.createClass({
  4. getInitialState: function@H_502_21@()@H_502_21@ {@H_502_21@
  5. return@H_502_21@ {
  6. value: this@H_502_21@.props.value
  7. };
  8. },plus: function@H_502_21@()@H_502_21@ {@H_502_21@
  9. this@H_502_21@.setState({
  10. value: this@H_502_21@.state.value + 1@H_502_21@
  11. });
  12. },minus: function@H_502_21@()@H_502_21@ {@H_502_21@
  13. this@H_502_21@.setState({
  14. value: this@H_502_21@.state.value - 1@H_502_21@
  15. });
  16. },render: function@H_502_21@()@H_502_21@ {@H_502_21@
  17. return@H_502_21@ (
  18. <Counter plus={this@H_502_21@.plus}
  19. minus={this@H_502_21@.minus}
  20. value={this@H_502_21@.state.value} />
  21. );
  22. }
  23. });
  24. module.exports = CounterContainer;

MyCounter

  1. var@H_502_21@ React = require@H_502_21@('react'@H_502_21@);
  2. var@H_502_21@ Counter = React.createClass({
  3. render@H_502_21@: function@H_502_21@() {
  4. return@H_502_21@ (
  5. <div>
  6. <span>{this@H_502_21@.props.value}</span>
  7. <button onClick={this@H_502_21@.props.plus}>+</button>
  8. <button onClick={this@H_502_21@.props.minus}>-</button>
  9. </div>
  10. );
  11. }
  12. });
  13. module@H_502_21@.exports@H_502_21@ = Counter;

index.js

  1. var@H_502_21@ React = require@H_502_21@('react'@H_502_21@);
  2. var@H_502_21@ ReactDOM = require@H_502_21@('react-dom'@H_502_21@);
  3. var@H_502_21@ MyCounterContainer = require@H_502_21@('./container/MyCounterContainer'@H_502_21@);
  4. ReactDOM.render(
  5. <MyCounterContainer value={0@H_502_21@} />,document@H_502_21@.getElementById('root'@H_502_21@)
  6. );

UI与逻辑分离成功,是不是感觉瞬间清爽许多。
关于什么时Contianer Component和Presentation Component,推荐此文

3.Use store to help dispatch actions

分离了UI后,的确逻辑上清楚了许多,但仔细观察会发现,上面的 MyCounterContainer 状态的改变只是两个button。而React认为Component的状态变化必定是由一个行为,即action造成的,因此,我们需要将上面的加减法抽象为一个行为驱动的事件,即一个行为对应一种状态结果。而 redux 就是干这事儿的,它通过createStore去创建一个store,这个store可以管理和知晓这个Component的状态,它通过dispatch分发action然后得到最新的状态结果。

利用store,我们将 MyCounterContainer 重构如下:

  1. var@H_502_21@ React = require@H_502_21@('react'@H_502_21@);
  2. var@H_502_21@ Counter = require@H_502_21@('../components/MyCounter'@H_502_21@);
  3. var@H_502_21@ createStore = require@H_502_21@('redux'@H_502_21@).createStore;
  4. var@H_502_21@ counter = function@H_502_21@(state,action)@H_502_21@ {@H_502_21@
  5. switch@H_502_21@ (action.type) {
  6. case@H_502_21@ 'PLUS'@H_502_21@:
  7. return@H_502_21@ state + action.value;
  8. case@H_502_21@ 'MINUS'@H_502_21@:
  9. return@H_502_21@ state - action.value;
  10. default@H_502_21@:
  11. return@H_502_21@ state;
  12. }
  13. };
  14. var@H_502_21@ store = createStore(counter,1000@H_502_21@);
  15. var@H_502_21@ CounterContainer = React.createClass({
  16. plus: function@H_502_21@()@H_502_21@ {@H_502_21@
  17. var@H_502_21@ nextState = store.dispatch({
  18. type: "PLUS"@H_502_21@,value: 2@H_502_21@
  19. });
  20. this@H_502_21@.setState(nextState);
  21. },minus: function@H_502_21@()@H_502_21@ {@H_502_21@
  22. var@H_502_21@ nextState = store.dispatch({
  23. type: "MINUS"@H_502_21@,render: function@H_502_21@()@H_502_21@ {@H_502_21@
  24. return@H_502_21@ (
  25. <Counter@H_502_21@ plus@H_502_21@={this.plus}@H_502_21@ minus@H_502_21@={this.minus}@H_502_21@ value@H_502_21@={store.getState()}@H_502_21@ />@H_502_21@ ); } }); module.exports = CounterContainer;@H_502_21@

4.Use connect to manage the dispatch and reducer

仔细观察上次重构,不难发现还是有些问题:

其一,尽管利用store帮我们管理了state,但是还是得我们手动setState,太过耦合。

其二,对于传递给子Component的参数,还是写死在Container里,不具有封装性和灵活性。

为了解决这个问题,我们可以利用 react-redux 的connect来解决
connect可以把自定义的state和dispatch分发事件绑定到Component上,其中mapStateToProps正如其名,可以将state作为Component的props传递下去;而mapDispatchToProps则可以把action触发逻辑传递下去,这样我们可以很灵活的传递功能事件了。

利用connect我们继续重构,MyCounterContainer 如下:

  1. var@H_502_21@ React = require@H_502_21@('react'@H_502_21@);
  2. var@H_502_21@ Counter = require@H_502_21@('../components/MyCounter'@H_502_21@);
  3. var@H_502_21@ createStore = require@H_502_21@('redux'@H_502_21@).createStore;
  4. var@H_502_21@ connect = require@H_502_21@('react-redux'@H_502_21@).connect;
  5. var@H_502_21@ mapStateToProps = function@H_502_21@(state)@H_502_21@ {@H_502_21@
  6. return@H_502_21@ {
  7. value: state
  8. }
  9. };
  10. var@H_502_21@ mapDispatchToProps = function@H_502_21@(dispatch)@H_502_21@ {@H_502_21@
  11. return@H_502_21@ {
  12. plus: function@H_502_21@()@H_502_21@ {@H_502_21@
  13. dispatch({
  14. type: "PLUS"@H_502_21@,value: 2@H_502_21@
  15. });
  16. },minus: function@H_502_21@()@H_502_21@ {@H_502_21@
  17. dispatch({
  18. type: "MINUS"@H_502_21@,value: 2@H_502_21@
  19. });
  20. }
  21. }
  22. };
  23. var@H_502_21@ CounterContainer = connect(
  24. mapStateToProps,mapDispatchToProps
  25. )(Counter);
  26. module.exports = CounterContainer;

5.Split actions

上面的例子已经很接近React的初级App的设计了,但当我们的Component特别复杂时,往往action也会难抽象,像上面的dispatch({type: “PLUS”,value: 2});偶合度太高,因为我们根本不知道这个action为什么是这样,就好比我们随便写了一个常量而并未定义任何变量名一样,别人是很难阅读的。因此比较好的做法是把action更小的分离,比如上面的action,我们可以分离成如下:

  1. module@H_502_21@.exports.plusAction = function@H_502_21@(val)@H_502_21@@H_502_21@ {
  2. return@H_502_21@ {
  3. type@H_502_21@: "PLUS"@H_502_21@,value: val
  4. };
  5. }
  6. module@H_502_21@.exports.minusAction = function@H_502_21@(val)@H_502_21@@H_502_21@ {
  7. return@H_502_21@ {
  8. type@H_502_21@: "MINUS"@H_502_21@,value: val
  9. };
  10. }

这样,在dispatch时,也会显得很简洁。

6.ES6 refactor

ES6部分就不在这里赘述了,大多都是语法问题,建议大家可以参考阮老师的书ES6入门

7.写在最后

个人认为,学习React十分不推荐一上手就用各种module,或者照猫画虎式的去填空,这样只能是到头来什么也不会。当你从头开始去理解时,才能找到痛点,而当你有痛点时你才需要重构,那么此时可能某个module就是你想要的。你用它只是为了省时,而不是你做不出来才用它。借用我前几天在知乎上回答的问题“用库丢脸不?”,我的观点是:用库不丢脸,不懂库还非要用库才丢脸原文请戳

猜你在找的React相关文章