yuxino / blog

🎬 Life's a Movie
17 stars 2 forks source link

React-in-patterns 笔记 #61

Closed yuxino closed 3 years ago

yuxino commented 6 years ago

React-in-patterns 我个人很喜欢这本。作者还是蛮风趣幽默的。同样English不是native language,人家可以写书,我只能看他的书 ¯(ツ)/¯

Also notice that English is not my native language. If you see a typo or something sounds weird please contribute here github.com/krasimir/react-in-patterns. If you are reading from a printed version of this book then feel free to use a pen ¯(ツ)/¯

书的知识点围绕着React的各种Pattern,不会很枯燥,倒不如说很有趣。虽然都非常主观,围绕做作者的视角。以下笔记不全出自React-in-patterns,而是在看了React-in-paterns之后想到的。更多的是我自己额外的一些研究和拓展。

Event handlers

我们在看官方文档的时候应该都有见过官方说过我们可以需要通过bind(this)来绑定事件,如果我们想访问component的this。比如一个Button组件。

它可能是这么写的

class Button extends React.Component {
   constructor (props) {
     super(props);
     this.state = { message: 'React in patterns' };
   }

  handlerClick () {
     console.log(this.state.message)
  }   

   render () {
     return (
       <button onClick={ this.handlerClick.bind(this) }>clike me</buton>
    )
  }
}

并且它也可能是这样的

class Button extends React.Component {
   constructor (props) {
     super(props)
     this.state = { message: 'React in patterns' }
     this.handlerClick = this.handlerClick.bind(this)
   }

  handlerClick () {
     console.log(this.state.message)
  }   

   render () {
     return (
       <button onClick={ this.handlerClick }>clike me</buton>
    )
  }
}

它们两个谁比较好,其实很显然当然是后者,因为在列表渲染多次重绘的时候,前者会不停的调用bind函数。后者不会只会做一次。

除了上面的情况以外。在构造器里面写bind能让我们使用一个方法处理多种输入。

class Form extends React.Component {
  constructor(props) {
    super(props);
    this._onNameChanged = this._onFieldChange.bind(this, 'name');
    this._onPasswordChanged = this._onFieldChange.bind(this, 'password');
  }
  render() {
    return (
      <form>
      <input onChange={ this._onNameChanged } />
      <input onChange={ this._onPasswordChanged }
      />
      </form>
    );
  }
  _onFieldChange(field, event) {
    console.log(`${ field } changed to ${ event.target.value }`);
  }
};

HOC

HOC的确很强大社区里也很流行。我个人对这个是有偏见的,我认为它不是最佳的实现。举个我不喜欢的原因,在React的devtools里面,高度组合过的HOC组件嵌套层级真的很深到了惨不忍睹的境界。

社区里最常用的HOC库应该是recompose。之前也写过一篇关于recompose的笔记。感兴趣的可以看一下。最基本的HOC的例子如下:

var enhanceComponent = (Component) =>
  class Enhance extends React.Component {
    render() {
      return (
        <Component {...this.props} style={{ border: '1px solid red' }} />
      )
    }
  };

var OriginalTitle = () => <h1>Hello world</h1>;
var EnhancedTitle = enhanceComponent(OriginalTitle);

class App extends React.Component {
  render() {
    return <EnhancedTitle />;
  }
};

这个例子展示的是给OriginTitle组件加一层外边框。

Function as a children, render prop

这一个之前很想写的一篇也没有写闲置了。在写那篇之前这里先整理一下吧。Function as children这个我认为是React里很强大的一个概念。Function as children或者是render prop都只是写法上的不同,本质上做的还是一件事情。

function TodoList({ todos, render }) {
  return (
    <section className='main-section'>
      <ul className='todo-list'>{
        todos.map((todo, i) => (
          <li key={ i }>{ render(todo) }</li>
        ))
      }</ul>
    </section>
  );
}

return (
  <TodoList
    todos={ todos }
    render={
      todo => isCompleted(todo) ?
        <b>{ todo.label }</b> : todo.label
    } />
);

另一个版本

function TodoList({ todos, render }) {
  return (
    <section className='main-section'>
      <ul className='todo-list'>{
        todos.map((todo, i) => (
          <li key={ i }>{ children(todo) }</li>
        ))
      }</ul>
    </section>
  );
}

return (
  <TodoList
    todos={ todos }>
    {
      todo => isCompleted(todo) ?
        <b>{ todo.label }</b> : todo.label
    }
 </TodoList>
);

他们其实都是一样的在做一样的事情。TodoList 完全不知道 label 和 status 属性。行为完全由父组件自定义。

我们可以抽出行为做出更为高级的抽象。React-Media就是一个很好的例子。

import React from "react";
import Media from "react-media";

class App extends React.Component {
  render() {
    return (
      <div>
        <Media query="(max-width: 599px)">
          {matches =>
            matches ? (
              <p>The document is less than 600px wide.</p>
            ) : (
              <p>The document is at least 600px wide.</p>
            )
          }
        </Media>
      </div>
    );
  }
}

还有其他的例子比如说一个权限验证的组件。如果用户具有读取产品列表的权限,那么我们便渲染 ProductList 。

<Authorize
  permissionsInclude={[ 'read:products' ]}
  render={ () => <ProductsList /> } />

Flux

Fulx并非react特有。首先我们来看官网

Flux is the application architecture that Facebook uses for building client-side web applications. It complements React's composable view components by utilizing a unidirectional data flow. It's more of a pattern rather than a formal framework, and you can start using Flux immediately without a lot of new code.

Flux是FaceBook用来构建客户端应用的架构。Flux通过使用单向数据流来补充React的可组合视图组件。它更多的是一种模式,而不是一个正式的框架,您可以立即开始使用Flux,而无需大量的新代码。

视频很有意思。 讲演的人是Jing Chen。中国人。这一张视频截图说明了传统MVC架构存在的问题。但是评论区也有表示FB提供的MVC的图是错误的,并且戏称Jing的做法完全不对,FB又一次发明了MVC,只是把他叫做了MVC。如果你对撕逼感兴趣转战评论区和reddit,还蛮有意思的。

image

好了言归正传。这个图的确有些问题的本人也是承认的。

Yeah, that was a tricky slide [the one with multiple models and views and bidirectional data flow], partly because there's not a lot of consensus for what MVC is exactly - lots of people have different ideas about what it is. What we're really arguing against is bi-directional data flow, where one change can loop back and have cascading effects.

但是是不妨碍我们理解。就照着Jing的话来说吧。那么上面的传统MVC架构关系非常复杂,MV双向的箭头意味着Model会改变View,同时View也会改变Model,这样在应用变得越来越复杂,特性越来越多的时候我们会非常难以追踪应用的变化状态。

这里需要补充一个例子。我个人认为我上面写的很不专业这一部分会搁置,直到找到足够多的资料。

ok 最近找到了资料其实MVC这种设计模式一直都是有很多变种,并且一直都不是很纯粹,时间证明了MVC 并不是web开发的最好实践。

一个更好的实践是MVVM。

参考资料