使用 React Hooks 重构 你的小程序

前端狗

2019/07/09 发布于 编程 分类

GMTC2019 

文字内容
1. l k EACR j s
4. c s 0 o @ P ING N >AP :EPT 6IR S - 0VSCHE 3 RA =REAM- y
5. W Wb W W W
7. i r 9 ./ 9 ./ u ./ 9 t s
8. EACR EACR W g W g W2 A ea f React Today and Tomorrow - Dan Abramov, React Conf 2018
9. SE 5SNCRI NA W9I IN A ED 1 8 d Wn g Wx EACR State of Vue - , VueConf CN 2019
10. @HV Mixins ❌ ❌ ❌
11. @HV Mixins HOC ✅ ❌ ✅ ❌ ❌ ❌ ❌
12. @HV Mixins HOC ✅ ❌ ✅ ✅ ❌ ❌ Hooks ❌ ❌ ✅ ✅ ✅
13. >AP Taro 1.3 QQ React Hooks Props Context CLI H5 Taro Doctor JSX
14. b
15. b Hooks React Taro Class state
16. S E=RARE Page({ data: { count: 0 }, increment () { this.setData({ count: this.data.count + 1 }) } }) counter.js <view> <text> You clicked {{count}} times !"text> <button bindtap="increment"> Click me !"button> !"view> counter.wxml
17. DARA T S E=RARE Page({ data: { count: 0 }, increment: () !# { this.setData({ count: this.data.count + 1 }) } }) counter.js ❌ <view> <text> You clicked {{count}} times !"text> <button bindtap="increment"> Click me !"button> !"view> counter.wxml
18. S E=RARE function Counter () { const [ count, setCount ] = useState(0) !$ / !$ function increment () { setCount(count + 1) } } return ( <View> <Text>You clicked {count} times!"Text> <Button onClick={increment}> Click me !"Button> !"View> ) counter.js
19. S E=RARE function Counter () { const [ count, setCount ] = useState(0) return ( <View> <Text>You clicked {count} times!"Text> <Button onClick={() !# setCount(count + 1)}> Click me !"Button> !"View> ) } counter.js
20. S E=RARE function useState<S> (initialState: S (() !# S)) : [S, Dispatch<SetStateAction<S!%] S Dispatch
21. Page({ data: { time: 60 }, start: false, toggleStart () { this.start = !this.start if (this.start) { this.interval = setInterval(() !# { this.setData({ time: this.data.time - 1 }) }, 1000) } else { clearInterval(this.interval) } }, onUnload () { clearInterval(this.interval) } }) count-down.js <view> <button bindtap="toggleStart"> {{time}} !"button> !"view> count-down.wxml
22. S E4FFECR function Counter () { const [ start, setStart ] = useState(false) const [ time, setTime ] = useState(60) useEffect(() !# { let interval if (start) { interval = setInterval(() !# { setTime(time - 1) }, 1000) } return () !# clearInterval(interval) }, [ start ]) } return ( <View> <Button onClick={() !# setStart(!start)}>{time}!"Button> !"View> )
23. function Counter () { const [ start, setStart ] = useState(false) const [ time, setTime ] = useState(60) useEffect(() !# { !$ effect let interval if (start) { interval = setInterval(() !# { setTime(time - 1) }, 1000) } return () !# clearInterval(interval) }, [ start ]) return ( <View> <Button onClick={() !# setStart(!start)}>{time}!"Button> !"View> ) }
24. function Counter () { const [ start, setStart ] = useState(false) const [ time, setTime ] = useState(60) useEffect(() !# { !$ effect let interval if (start) { interval = setInterval(() !# { setTime(time - 1) }, 1000) } return () !# clearInterval(interval) }, [ start ]) !$ effect return ( <View> <Button onClick={() !# setStart(!start)}>{time}!"Button> !"View> ) }
25. function Counter () { const [ start, setStart ] = useState(false) const [ time, setTime ] = useState(60) useEffect(() !# { !$ effect let interval if (start) { interval = setInterval(() !# { setTime(time - 1) }, 1000) } return () !# clearInterval(interval) !$ clean-up }, [ start ]) !$ effect return ( <View> <Button onClick={() !# setStart(!start)}>{time}!"Button> !"View> ) }
26. function Counter () { const [ start, setStart ] = useState(false) const [ time, setTime ] = useState(60) useEffect(() !# { !$ effect let interval if (start) { interval = setInterval(() !# { setTime(time - 1) !$ ❌ ❌ ❌ }, 1000) } return () !# clearInterval(interval) !$ clean-up }, [ start ]) !$ effect return ( <View> <Button onClick={() !# setStart(!start)}>{time}!"Button> !"View> ) }
27. function Counter () { const [ start, setStart ] = useState(false) const [ time, setTime ] = useState(60) useEffect(() !# { !$ effect let interval if (start) { interval = setInterval(() !# { setTime(time - 1) !$ ❌ time effect }, 1000) } return () !# clearInterval(interval) !$ clean-up }, [ start ]) !$ effect return ( <View> <Button onClick={() !# setStart(!start)}>{time}!"Button> !"View> ) }
28. function Counter () { const [ start, setStart ] = useState(false) const [ time, setTime ] = useState(60) useEffect(() !# { !$ effect let interval if (start) { interval = setInterval(() !# { setTime(t !# t - 1) !$ ✅ setTime }, 1000) } return () !# clearInterval(interval) !$ clean-up }, [ start ]) !$ effect return ( <View> <Button onClick={() !# setStart(!start)}>{time}!"Button> !"View> ) } state
29. function Counter () { const [ start, setStart ] = useState(false) const [ time, setTime ] = useState(60) const currentTime = useRef(time) !$ useEffect(() !# { !$ effect let interval if (start) { interval = setInterval(() !# { setTime(currentTime.current!') !$ currentTime.current }, 1000) } return () !# clearInterval(interval) !$ clean-up }, [ start ]) !$ effect } return ( <View> <Button onClick={() !# setStart(!start)}>{time}!"Button> !"View> )
30. function Counter () { const [ start, setStart ] = useState(false) const [ time, setTime ] = useState(60) const interval = useRef() !$ interval useEffect(() !# { !$ effect if (start) { interval.current = setInterval(() !# { setTime(t !# t - 1) !$ ✅ setTime }, 1000) } return () !# clearInterval(interval.current) !$ clean-up }, [ start ]) !$ effect return ( <View> <Button onClick={() !# setStart(!start)}>{time}!"Button> !"View> ) } state
31. g m <view> <child !( !"view> page.wxml <view> <counter !( !"view> child.wxml <view> <text> You clicked {{count}} times !"text> <butto bindtap="increment"> Click me !"button> !"view> counter.wxml
32. g m <view> <child count="{{count}}" bind:increment="increment" !( !"view> Page({ data: { count: 0 }, increment () { this.setData({ count: this.data.count + 1 }) } }) <view> <counter count="{{count}}" bind:increment="increment" !( !"view> Component({ properties: { count: Number }, methods: { increment () { this.triggerEvent('increment') } } }) <view> <text> You clicked {{count}} times !"text> <butto bindtap="increment"> Click me !"button> !"view> Component({ properties: { count: Number }, methods: { increment () { this.triggerEvent('increment') } } })
33. S E2 NRE R export const CounterContext = Taro.createContext(null); const App = () !# { const [ count, setCount ] = useState(0) return ( <CounterContext.Provider value={{ count, setCount }}> <Child !( !"CounterContext.Provider> ); } const Child = () !# ( <View> <Counter !( !"View> ); const Counter = () !# { const { count, setCount } = useContext(CounterContext) return ( <View> <Text> You clicked {count} times !"Text> <Button onClick={() !# setCount(count + 1)} > Click me !"Button> !"View> ) }
34. S E2 NRE R export const CounterContext = Taro.createContext(null); const App = () !# { const [ count, setCount ] = useState(0) return ( <CounterContext.Provider value={{ count, setCount }}> <Child !( !"CounterContext.Provider> ); } class Child extends Component { shouldComponentUpdate () { return false } } render () { return ( <View> <Counter !( !"View> ) } const Counter = () !# { const { count, setCount } = useContext(CounterContext) return ( <View> <Text> You clicked {count} times !"Text> <Button onClick={() !# setCount(count + 1)} > Click me !"Button> !"View> ) }
36. !$ function EditableText ({ title }) { const [ editing, setEditing ] = useState(false) } return ( <View> { editing ? <TextInput editing={editing} !( : <Text onDoubleClick={() !# setEditing(true)} > {title} !"Text> } !"View> )
37. !$ function EditableText ({ title }) { const [ editing, setEditing ] = useState(false) } return ( <View> { editing ? <TextInput editing={editing} !( : <Text onDoubleClick={() !# setEditing(true)} /** ❌ > {title} !"Text> } !"View> ) !)
38. function EditableText ({ title }) { const [ lastClickTime, setClickTime ] = useState(0) const [ editing, setEditing ] = useState(false) return ( <View> { editing ? <TextInput editing={editing} !( : <Text onClick={e !# { const currentTime = e.timeStamp const gap = currentTime - lastClickTime if (gap > 0 !* gap < 300) { !$ double click setEditing(true) } setClickTime(currentTime) }} > {title} !"Text> } !"View> ) }
39. function useDoubleClick (cb) { const [ lastClickTime, setClickTime ] = useState(0) } function EditableText ({ title }) { const [ editing, setEditing ] = useState(false) const textOnDoubleClick = useDoubleClick( () !# setEditing(true) ) return (e) !# { const currentTime = e.timeStamp const gap = currentTime - lastClickTime if (gap > 0 !* gap < 300) { cb !* cb(e) } setClickTime(currentTime) } } return ( <View> { editing ? <TextInput editing={editing} !( : <Text onClick={textOnDoubleClick} > {title} !"Text> } !"View> )
40. function useDoubleClick (cb) { const [ lastClickTime, setClickTime ] = useState(0) } function EditableText ({ title }) { const textOnDoubleClick = useDoubleClick(300, () !# setEditing(true) !$ ❌ setEditing is not defined ) const [ editing, setEditing ] = useState(false) return ( <View> { editing ? <TextInput editing={editing} !( : <Text onClick={textOnDoubleClick} > {title} !"Text> } !"View> ) return (e) !# { const currentTime = e.timeStamp const gap = currentTime - lastClickTime if (gap > 0 !* gap < 300) { cb !* cb(e) } setClickTime(currentTime) } }
41. function EditableText ({ title }) { const [ editing, setEditing ] = useState(false) const textOnDoubleClick = useDoubleClick(300) function useDoubleClick (cb) { const [ lastClickTime, setClickTime ] = useState(0) } return ( <View> { editing ? <TextInput editing={editing} !( : <Text onClick={textOnDoubleClick(() !# setEditing(true) )} > {title} !"Text> } !"View> ) return (e) !# { const currentTime = e.timeStamp const gap = currentTime - lastClickTime if (gap > 0 !* gap < 300) { cb !* cb(e) } setClickTime(currentTime) } }
42. function EditableText ({ title }) { const [ editing, setEditing ] = useState(false) const textOnDoubleClick = useDoubleClick() function useDoubleClick () { const [ lastClickTime, setClickTime ] = useState(0) } return (callback) !# (e) !# { const currentTime = e.timeStamp const gap = currentTime - lastClickTime if (gap > 0 !* gap < 300) { callback !* callback(e) } setClickTime(currentTime) } } return ( <View> { editing ? <TextInput editing={editing} !( : <Text onClick={textOnDoubleClick(() !# setEditing(true) )} > {title} !"Text> } !"View> )
43. function EditableText ({ title }) { const textOnDoubleClick = useDoubleClick() const buttonOnDoubleClick = useDoubleClick() !$ } return ( <View> <Text onClick={textOnDoubleClick(!!+)}> {title} !"Text> <Button onClick={buttonOnDoubleClick(!!+)} !( !"View> )
44. hv class Numbers extends Component { shouldComponentUpdate () { return false } render () { return <View> { expensive(this.props.array) .map(i !# <View>{i}!"View>) } !"View> } }
45. hv class Numbers extends Component { shouldComponentUpdate () { return false } render () { return <View> { expensive(this.props.array) .map(i !# <View>{i}!"View>) } !"View> } } function Numbers ({ array }) { return ( <View> { expensive(array).map( i !# <View>{i}!"View> ) } !"View> ) } export default Taro.memo(Numbers, () !# true)
46. hv function Counter () { const [ count, setCount ] = useState(0) const [val, setValue] = useState('') function expensive() { let sum = 0 for (let i = 0; i < count * 1e9; i!,) { sum += i } return sum } } return ( <View> <Text>You clicked {expensive()} times}!"Text> <Button onClick={() !# setCount(count + 1)}> Click me !"Button> <Input value={val} onChange={event !# setValue(event.detail.value)} !( !"View> ) ❌ function Counter () { const [ count, setCount ] = useState(0) const [val, setValue] = useState('') const expensive = useMemo(() !# { let sum = 0 for (let i = 0; i < count * 1e9; i!,) { sum += i } return sum }, [count]) } return ( <View> <Text>You Clicked {expensive} times!"Text> <Button onClick={() !# setCount(count + 1)}> Click me !"Button> <Input value={val} onChange={event !# setValue(event.detail.value)} !( !"View> )
47. hv function Counter () { const [ count, setCount ] = useState(0) const [val, setValue] = useState('') function expensive() { let sum = 0 for (let i = 0; i < count * 1e9; i!,) { sum += i } return sum } } function Counter () { const [ count, setCount ] = useState(0) const [val, setValue] = useState('') const expensive = useMemo(() !# { let sum = 0 for (let i = 0; i < count * 100; i!,) { sum += i } return sum }, [count]) !$ ✅ count return ( <View> <Text>You clicked {expensive()} times}!"Text> <Button onClick={() !# setCount(count + 1)}> Click me !"Button> <Input value={val} onChange={event !# setValue(event.detail.value)} !( !"View> ) } ❌ return ( <View> <Text>You Clicked {expensive} times!"Text> <Button onClick={() !# setCount(count + 1)}> Click me !"Button> <Input value={val} onChange={event !# setValue(event.detail.value)} !( !"View> )
48. hv memo !" memoization memoization
49. React Redux useSelector() useDispatch() useStore() react-redux@7 useState() react-redux.js.org/next/api/hooks useContext() useEffect() useMemo() connect()
52.
53. Hooks
54. • • Hooks React • React Hooks • Hooks
55. const CurrentOwner: { current: null Component<any, any>, index: number } = { !$ Taro , !$ current: null, !$ Taro hooks !$ Hook index: 0 }
56. const CurrentOwner: { current: null Component<any, any>, index: number } = { !$ Taro , !$ current: null, !$ Taro hooks !$ Hook index: 0 } React.!-SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner
57. function getHook (): Hook { if (CurrentOwner.current !!/ null) { throw new Error(`invalid hooks call: hooks can only be called in a taro component.`) } const index = CurrentOwner.index!, !$ hook Taro ID const hooks: Hook[] = CurrentOwner.current.hooks !$ hooks if (index !0 hooks.length) { !$ hook hooks.push({} as Hook) !$ hook } return hooks[index] !$ hook }
58. function useState<S> (initialState: S (() !# S)): [S, Dispatch<SetStateAction<S!%] { if (isFunction(initialState)) { !$ initialState initialState = initialState() !$ } const hook = getHook() as HookState<S> !$ hook if (isUndefined(hook.state)) { !$ hook hook.component = Current.current! !$ Taro hook.state = [ !$ hook.state initialState, (action) !# { hook.state[0] = isFunction(action) ? action(hook.state[0]) : action enqueueRender(hook.component) !$ } ] } return hook.state !$ hook }
60. React create-react-app eslint-plugin-hooks prepack prettier
61. C Taro esling-plugin-taro Taro Doctor Taro Taro
62. https:!$taro.jd.com
65. THANKS THANKS! THANKS!