React 的過去 現在 未來

Before React.js 

Separation Concern

Model

View

Rethinking best practices - Pete Hunt JSConf EU 2013

Template separate technology, but not concern

  • Dispaly logic and markup 無可避免的高耦合
  • Dispaly logic and markup 高度聚合

Model

View

Breakdown MVC into Component

  • 重複利用 html DOM tree 的概念
  • 容易針對相同的需求share same code
  • 宣告式 ui 單一資料流

React to Single Source of Truth, not imperative communication

view and behavior should be predictable

view = f(state)

Encourage immutable variable

  • shouldComponentUpdate shallow compare
  • variable is consistent as it been declared 減少 tracing code 的認知負擔

What in React still sucks

  • huge component

Logic separated in multiple lifecycle method

class ActInfo extends Component {
  componentDidMount() {
    this.fetchActInfo()
    this.startTimer()
    this.fixWindow()
  }

  componentWillUnmount() {
    this.cancelFetchActInfo()
    this.clearTimer()
    this.unFixWindow()
  }
  
  componentDidUpdate(prevProps) {
    if(this.props.aaa !== prevProps.aaa) {
      ...
    }
  }
}

What in React still suck

  • huge component
  • confusing class

Confusing class

  • binding this in class is confusing for beginner
  • functional component => class component
  • hard to optimize for compiler

What in React still suck

  • huge component
  • confusing class
  • reuse logic

Sharing Component

view(html css) interaction render logic
 ✅  ✅
 ✅

stateful Component

stateless component

❔❔

Mixins in React.createClass

Mixins

ComponetA

ComponetB

const MouseMixin = {
  getInitialState() {
    return { x: 0, y: 0 }
  },
  handleMouseMove(event) {
    this.setState({
      x: event.clientX,
      y: event.clientY
    })
  }
}

const App = React.createClass({
  mixins: [ MouseMixin ],
  render() {
    const { x, y } = this.state
    return (
      <div 
        style={{ height: '100%' }} 
        onMouseMove={this.handleMouseMove}
      >
        <h1>The mouse position is ({x}, {y})</h1>
      </div>
    )
  }
})

However

 

const App = React.createClass({
  // Use the mixin!
  mixins: [ 
    MouseMixin,
    toolTipsMixin,
    popUpMixin
  ],
  
  render() {
    const { x, y } = this.state

    return (
      <div 
        style={{ height: '100%' }} 
        onMouseMove={this.handleMouseMove}
      >
        <h1>The mouse position is ({x}, {y})</h1>
      </div>
    )
  }
})

Mixins Considered harmful

  • mixins 與 mixins 之間以及 mixins 與 component 之間的隱式依賴讓程式變得難以理解跟解耦合
  • life cycle method 也可以被 mixins 劫持
  • 命名衝突
  • ES2015 class 並不支援 mixins

Higher Order Component

Higher order function
高階函式

  • takes one or more functions as arguments
  • return a function as its result
f=g(h(x))

Higher Order Component aka HOC

  • takes a component as arguments
  • return a component as its result
  • pass sharing logic method and data through props

Componnt

Componnt

hoc

hoc

const withFeature = (Cmp) => {
    class Wrapper extends Component {
      componentDidMount () { ... }

      componentWillUnmount () { ... }

      componentDidUpdate () { ... }
      
      shareMethod = () => {}
      
      state = {
        shareData: 'initial'
      }

      render() {
        <Cmp
          data={this.state.shareData}
          method={this.shareMethod} 
        />
      }
    }
    return Wrapper
}

export default withFeature(Cmp)

Pros

  • vertical seperation concern
  • eazy to use hoc(Component)
  • eazy to extends hocA(hocB(Component))

Example

  • react-redux connect
  • react-i13n createI13n
  • recompose pure

However

Never write another Hoc
by Michael Jackson Phoenix React meetup

Never write another HOC
by Michael Jackson

  • when multiple hocs embed
  • where is your prop come from?
  • naming conflict
const ComponentA = ({ doSomeThing }) => (
  <div onClick={doSomeThing} />
)

compose(
  withRouter
  withContext
  connect
  withI13n
  withI18n
)(ComponentA)

render Props

  • call back function as a props (often as child)
  • sharing method or data in the argument
const App = () => (
  <MouseHover>
    {(handleMouseHover, isMouseHover) => (
      <div
       onMouseHover={handleMouseHover}
       className={
         isMouseHover ? style.active : style.normal
       } 
      />
    )}
  <MouseHover>
)

Pros

  • dependency is clear 
  • no more naming conflict 
const App = () => (
  <Tooltip>
   {(renderToolTip)=> (
      <MouseHover>
        {(handleMouseHover, isMouseHover) => (
          <div
           onMouseHover={handleMouseHover}
           className={
             isMouseHover ? style.active : style.normal
           } 
          >
           {renderToolTip()}
          </div>
        )}
      <MouseHover>
   )}
  </Tooltip>
)

Example

  • React 16 context api
  • React Router
  • Apollo client

However

Git diff

Wrapper hell + Multiple call React .createElement

What in react still sucks

  • giant Component
    • logic is separated in mount and update lifecycle
    • watch some props in update lifecycle
  • confusing class
    • this binding is confusing for beginner
    • hard for compiler optimize
    • functional component => class component
  • reuse logic
    • wrapper hell + multiple createElement call
    • git diff
    • declarative and explicit dependency
    • naming conflict

React hooks

make Functional Component great again

useState Demo

useEffect Demo

customer hook

minify comparison

hoc

babel loose compile 3.8KB
minify 2.5KB
0.66%

hooks
babel loose compile 1.4KB

minify  684 Byte

0.48%

What in react still sucks

  • giant Component
    • logic is separated in mount and update lifecycle
    • watch some props in update lifecycle
  • confusing class
    • this binding is confusing for beginner
    • hard for compiler optimize
    • functional component => class component
  • reuse logic
    • wrapper hell + multiple createElement call
    • git diff
    • declarative and explicit dependency
    • naming conflict

😀

😀

😀

😀

😀

😀

😀

😀

😀

How hooks works

pseudo code

let hooks = null;

export function useHook() {
  hooks.push(hookData); 
}

function reactInternalRenderAComponentMethod(component) {
  hooks = [];
  component();
  let hooksForThisComponent = hooks;
  hooks = null;
}

Rule of hooks

  • top level
  • no condition
  • every render always the same order hook
  • only call hooks inside
  • eslint plugin

controversial?

More hooks

  • Basic Hooks

    • useState
    • useEffect
    • useContext
  • Additional Hooks

    • useReducer
    • useCallback
    • useMemo
    • useRef
    • useImperativeMethods
    • useMutationEffect
    • useLayoutEffect
Made with Slides.com