React组件重构实践

基础应用

一个可以搜索、删除书籍的单页面:


6464570-3c1bf2847a651eb1.png
image.png

基础代码以create react app生成,使用ES6的语法特征进行重构。

基础代码

整个页面不做任何组件拆分,只有一个BookListing组件:

const list=[
  {
    title: 'Road to React',
    url: 'https://facebook.github.io/react/',
    author: 'Jordan Walke',
    num_comments: 3,
    points: 4,
    objectID: 0,
  },
  ...
];

const isSearched = function(searchTerm){
  return function(item) {
    return item.title.toLowerCase().includes(searchTerm.toLowerCase());
  }
}
export default class BookListing extends Component {

  constructor(props) {
    super(props);
    this.state = {
      list,
      searchTerm: ''
    };

    this.search = this.search.bind(this);
    this.dismiss = this.dismiss.bind(this);
  }

  search(e) {
    this.setState({
      searchTerm: e.target.value
    });
  }

  dismiss(objectID) {
    let updatedList = this.state.list.filter(item => item.objectID !== objectID);
    this.setState({
      list: updatedList
    });
  }

  render() {
    return (
      <div>
        <form>
          Search <input
            type="text"
            onChange={e => this.search(e)}
          >
          </input>
        </form>
        <div>
          {this.state.list.filter(isSearched(this.state.searchTerm))
            .map(item =>
              <div key={item.objectID} className="table-row">
                <span style={{ width: '40%' }}>
                  <a href={item.url}>{item.title}</a>
                </span>
                <span style={{ width: '30%' }}>{item.author}</span>
                <span style={{ width: '10%' }}>{item.num_comments}</span>
                <span style={{ width: '10%' }}>{item.points}</span>
                <span style={{ width: '10%' }}>
                  <button
                    type="button"
                    onClick={() => this.dismiss(item.objectID)}
                  >
                    Dismiss
            </button>
                </span>
              </div>
            )}
        </div>
      </div>
    )
  }
}

重构实践

  • ES6箭头函数

isSearched函数可以使用ES6的箭头函数语法进行重构,改写后:

const isSearched = searchTerm => item => item.title.toLowerCase().includes(searchTerm.toLowerCase())

仅用一行就可以完成同样的功能。

  • 解构

ES6支持对象解构,render函数中的state可以解构,简化链式调用的长度:

render() {
  const {list, searchTerm} = this.state;
  // ...
  {list.filter(isSearched(searchTerm))
    .map(item => 
      <div>
        {/* ... */}
      </div>
  )}
}
  • 组件拆分

BookListing组件过于复杂,可以拆分成几个职责更单一的组件,提取Search,Table组件,对应于搜索框和列表:

export default class Search extends Component {
  render() {
    const {value, search, children} = this.props;
    return (
      <form>
        Search <input
          type="text"
          onChange={search}
        >
        </input>
      </form>
    )
  }
} 
export default class Table extends Component {
  render() {
    const { list, pattern, onDismiss } = this.props;
    return (
      <div>
        {list.filter(isSearched(searchTerm))
          .map(item =>
            <div key={item.objectID} className="table-row">
              <span style={{ width: '40%' }}>
                <a href={item.url}>{item.title}</a>
              </span>
              <span style={{ width: '30%' }}>{item.author}</span>
              <span style={{ width: '10%' }}>{item.num_comments}</span>
              <span style={{ width: '10%' }}>{item.points}</span>
              <span style={{ width: '10%' }}>
                <button
                  type="button"
                  onClick={() => this.dismiss(item.objectID)}
                >
                  Dismiss
                </button>
              </span>
            </div>
          )}
      </div>
    )
  }
}

改写顶层组件如下:

export default class extends Component {
  //...

  render() {
    <div className="page">
      <Search
        value={searchTerm}
        onChange={this.search}
      />
      <Table
        list={list}
        pattern={searchTerm}
        onDismiss={this.dismiss}
      />      
    </div>
  }
}

button也可以抽取成为单独的组件:

export default class Button extends Component {
  render() {
    return(
      <button
        type="button"
        onClick={onClick}
      >
        {value}
      </button>
    )
  }
}
  • 使用children属性

React组件的props提供了children属性,代表组件的子元素,可以对Search,Button组件使用:

<Search
  value={searchTerm}
  onChange={this.search}
>
  Search
</Search>
// use children
export default class Search extends Component {
  render() {
    const {value, onChange, children} = this.props;
    return (
      <form>
        {children} <input
          type="text"
          onChange={onChange}
        >
        </input>
      </form>
  }
}
  • 引入无状态组件

以上提取的3个组件,基本都不需要维护自身状态,可以改写为无状态组件:

export default function Search(props) {
  const { value, onChange, children } = props;
  return (
    <form>
      {children} <input
        type="text"
        value={value}
        onChange={onChange}
      />
    </form>
  )
}

另外两个组件也可以做同样的处理。无状态组件除了语法简洁、便于测试等优点外,无状态的特性使得它没有React组件的生命周期管理,因此在渲染时相比于React组件有更好的性能。

  • 更纯的函数式组件

props可以通过解构直接当做函数参数,然后直接加上箭头函数进行改造,省掉函数内部的props参数解析,并且直接去掉return关键字:

export default ({ value, onChange, children }) =>
  <form>
    {children} <input
      type="text"
      value={value}
      onChange={onChange}
    />
  </form>

以上处理也同样适用于另外两个提取的组件。

猜你喜欢

转载自blog.csdn.net/weixin_33897722/article/details/86873701