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
Reactjs 過去 現在 未來
By zeroshine
Reactjs 過去 現在 未來
- 453