34
  1. Async Nature Of setState()
  2. Dependency Injection
  3. Context Wrapper
  4. Event Handlers
  5. Flux pattern
  6. One way data flow
  7. Presentational and Container components
  8. Third Party Integration
  9. Passing a function to setState
  10. Decorators
  11. Feature Flags
  12. The switching component
  13. Reaching into a Component
  14. Lists Components
  15. Components for formatting text
  16. Share Tracking Logic

github地址:https://github.com/vasanthk/react-bits

我们接着上面的文章说下去。

Async Nature Of setState()

组件内的setState方法异步的,意味着调用该方法后马上获取setState值的话,往往拿到的是原始值。


//我们往往需要在值改变之后执行一些操作,可以通过setState的回调函数操作
_onClickHandler: function _onClickHandler() {
   console.log(this.state.count);
   this.setState({
     count: this.state.count + 1
   }, () => {
   console.log(this.state.count);
   });
}

//网上也有另外一个方法是通过Async/Await实现异步转同步
setStateAsync(state) {
  return new Promise((resolve) => {
    this.setState(state, resolve)
  });
}
async componentDidMount() {
  await this.setStateAsync({count: this.state.count + 1});
  console.log(this.state.count);
}


Dependency Injection

依赖注入听似一个抽象的概念,实际在React中,依赖注入很容易看到。


// Title.jsx
export default function Title(props) {
  return <h1>{ props.title }</h1>;
}

// Header.jsx
import Title from './Title.jsx';
export default function Header() {
  return (
    <header>
      <Title />
    </header>
  );
}

// App.jsx
import Header from './Header.jsx';
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { title: 'React Dependency Injection' };
  }
  render() {
    return <Header />;
  }
}

Context Wrapper

原来Context并非只是一个普通对象那么简单,然而实际中并不常用Context


// dependencies.js
export default {
  data: {},
  get(key) {
    return this.data[key];
  },
  register(key, value) {
    this.data[key] = value;
  }
}

// App.js
import dependencies from './dependencies';
dependencies.register('title', 'React in patterns');

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

App.childContextTypes = {
  data: React.PropTypes.object,
  get: React.PropTypes.func,
  register: React.PropTypes.func
};

// Title.jsx
export default class Title extends React.Component {
  render() {
    return <h1>{ this.context.get('title') }</h1>
  }
}
Title.contextTypes = {
  data: React.PropTypes.object,
  get: React.PropTypes.func,
  register: React.PropTypes.func
};


Event Handlers

React里的事件处理有一个this指向问题,完成一个事件绑定推荐写法如下


class Switcher extends React.Component {
  constructor(props) {
    super(props);
    this.state = { name: 'React in patterns' };
    this._buttonClick = this._handleButtonClick.bind(this);
  }
  render() {
    return (
      <button onClick={ this._buttonClick }>
        click me
      </button>
    );
  }
  _handleButtonClick() {
    console.log(`Button is clicked inside ${ this.state.name }`);
  }
}

//但上面这种写法无法传递额外参数,下面的写法可以
<button onClick={ this._handleButtonClick.bind(this) }>
  click me
</button>


Flux pattern

Flux模式,待续…

One way data flow

单向数据流,待续…

Presentational and Container components


//数据和逻辑一起
class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {time: this.props.time};
    this._update = this._updateTime.bind(this);
  }

  render() {
    var time = this._formatTime(this.state.time);
    return (
      <h1>{ time.hours } : { time.minutes } : { time.seconds }</h1>
    );
  }

  componentDidMount() {
    this._interval = setInterval(this._update, 1000);
  }

  componentWillUnmount() {
    clearInterval(this._interval);
  }

  _formatTime(time) {
    var [ hours, minutes, seconds ] = [
      time.getHours(),
      time.getMinutes(),
      time.getSeconds()
    ].map(num => num < 10 ? '0' + num : num);

    return {hours, minutes, seconds};
  }

  _updateTime() {
    this.setState({time: new Date(this.state.time.getTime() + 1000)});
  }
}

ReactDOM.render(<Clock time={ new Date() }/>, ...);



//数据和逻辑分离

// Clock/index.js
import Clock from './Clock.jsx'; 

export default class ClockContainer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {time: props.time};
    this._update = this._updateTime.bind(this);
  }

  render() {
    return <Clock { ...this._extract(this.state.time) }/>;
  }

  componentDidMount() {
    this._interval = setInterval(this._update, 1000);
  }

  componentWillUnmount() {
    clearInterval(this._interval);
  }

  _extract(time) {
    return {
      hours: time.getHours(),
      minutes: time.getMinutes(),
      seconds: time.getSeconds()
    };
  }

  _updateTime() {
    this.setState({time: new Date(this.state.time.getTime() + 1000)});
  }
};

// Clock/Clock.jsx
export default function Clock(props) {
  var [ hours, minutes, seconds ] = [
    props.hours,
    props.minutes,
    props.seconds
  ].map(num => num < 10 ? '0' + num : num);

  return <h1>{ hours } : { minutes } : { seconds }</h1>;
};


Third Party Integration

讲述React和jQuery的结合


//节选其中一部分代码
class Tags extends React.Component {
  //在这里获取到DOM,并且初始化jQuery插件
  componentDidMount() {
    this.list = $(this.refs.list);
    this.list.tagit();
  }

  //因为是单渲染,数据展示依赖于jQuery,无需React动态更新,这里可以返回false
  shouldComponentUpdate() {
    return false;
  }
  
  //当Props数据发生变化时,再初始化jQuery插件
  componentWillReceiveProps(newProps) {
    this.list.tagit('createTag', newProps.newTag);
  }

  render() {
    return (
      <ul ref='list'>
        { this.props.tags.map((tag, i) => <li key={ i }>{ tag } </li>) }
      </ul>
    );
  }
}

Passing a function to setState

setState使用函数返回


  //以下同时执行时,实际返回值并非期待值
  // assuming this.state.count === 0
  this.setState({count: this.state.count + 1});
  this.setState({count: this.state.count + 1});
  this.setState({count: this.state.count + 1});
  // this.state.count === 1, not 3

  //为解决这个问题,使用以下方法
  this.setState((prevState, props) => ({
    count: prevState.count + props.increment
  }));

  //变异的方式
  //传递对象
  this.setState({ expanded: !this.state.expanded });

  //传递函数
  this.setState(prevState => ({ expanded: !prevState.expanded }));


Decorators

使用ES6的装饰器,待续…

Feature Flags

结合Redux使用,待续…

The switching component

切换组件,这是一种临时做法,实际中都是通过路由控制的


import HomePage from './HomePage.jsx';
import AboutPage from './AboutPage.jsx';
import UserPage from './UserPage.jsx';
import FourOhFourPage from './FourOhFourPage.jsx';

const PAGES = {
  home: HomePage,
  about: AboutPage,
  user: UserPage
};

const Page = (props) => {
  const Handler = PAGES[props.page] || FourOhFourPage;

  return <Handler {...props} />
};

//验证
Page.propTypes = {
  page: PropTypes.oneOf(Object.keys(PAGES)).isRequired
};


Reaching into a Component

从父元素访问组件


  //子组件
class Input extends Component {
  focus() {
    this.el.focus();
  }

  render() {
    return (
      <input
        ref={el=> { this.el = el; }}
      />
    );
  }
}  

  //父组件
class SignInModal extends Component {
  componentDidMount() {
    //请注意,当您对组件使用ref时,它是对组件的ref(而不是底层元素)
    //因此您可以访问其方法。
    this.InputComponent.focus();
  }

  render() {
    return (
      <div>
        <label>User name:</label>
        <Input
          ref={comp => { this.InputComponent = comp; }}
        />
      </div>
    )
  }
}


Lists Components

列表组件


//可以把数组展示的函数单独出来,而不是写组件中去,这个函数可以达到共用。
const SearchSuggestions = (props) => {
  const renderSearchSuggestion = listItem => (
    <li key={listItem.id}>{listItem.name} {listItem.id}</li>
  );

  return (
    <ul>
      {props.listItems.map(renderSearchSuggestion)}
    </ul>
  );
};


Components for formatting text

格式化文本的组件,待续…

Share Tracking Logic

使用高阶组件分享跟踪逻辑,待续…

转载原创文章请注明,转载自: 老赵茶馆 » React的模式,技术,技巧–02

留言

Loading...

发表评论

电子邮件地址不会被公开。 必填项已用*标注

  1. 喜帖街 谢安琪
  2. 猫の恩返 室内管弦楽団
Pluto主题Github源码公开
为学习交流,本主题源码已在Github公开,支持请购买正版。