# 從 jQuery 到 React
###### tags: `KK 工具箱`
## 05/30(四)18:30 - 21:30
### 環境建置
* 推薦使用 [iTerm2](https://www.iterm2.com/) + [oh-my-zsh](https://ohmyz.sh/)
* cd
* ls
* rm
* mv
* 推薦使用 [VS Code Editor](https://code.visualstudio.com/)
* 推薦插件
* prettier
* vscode-icons
* path intellisense
* npm intellisense
* code spell checker
* 推薦使用 [yarn](https://yarnpkg.com) 套件管理工具
* yarn v.s. npm install
* yarn (global) add v.s. npm install {pkg}
* yarn remove v.s. npm uninstall {pkg}
* yarn upgrade v.s. npm update/upgrade
* 推薦使用 create-react-app(CRA) 腳手架
* create-react-app todolist
### jQuery v.s. React
* 模組化
* 協作性
* 高效能
### React 設計理念
Virtual DOM

### React 專案結構
* 運作原理

* 專案結構
```
.
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
├── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ └── serviceWorker.js
├── README.md
├── package.json
└── yarn.lock
```
> 進階:大型 React App 檔案架構
> 1. Organize by features
> 2. Create strict module boundaries
> 3. Avoid circular dependencies
```
CSS -> CSS Module / Styled Component
src
├── images
│ ├── icons
│ │ ├── icon-menu.svg
│ │ └── icon-user.svg
│ ├── brands
│ │ ├── logo.svg
│ │ └── logo-w.svg
│ └── mockups
│ ├── mockup-user.png
│ └── mockup-banner.png
├── components
│ ├── auth
│ │ ├── AuthForm.js
│ │ └── AuthModal.js
│ └── common
│ ├── App.js
│ ├── Modal.js
│ └── buttons
│ ├── Button.js
│ └── SocialButton.js
├── index.js
└── serviceWorker.js
```
## 06/06(四)18:30 - 21:30
### 穩住 JS
#### callback
```javascript=
function callback() {
// do something
}
callback // 紙條
callback() // 打電話
// 常見模式 1: error-first
some_func(arg1, arg2, function(error, result) {
// do something
})
// 常見模式 2: split callback
some_func(arg1, arg2, function(result) {
// handle success
}, function(error) {
// handle error
})
```
```javascript=
function add(a, b, callback) {
try {
var c = a / b;
callback(null, c);
} catch(error) {
callback(error);
}
}
```
```javascript=
// quiz
function foo(name, cb) {
console.log(name);
cb(null);
}
function cb(error, result) {
console.log(error, result);
}
foo('KK', cb);
```
#### this
```javascript=
// 誰叫我?
function foo() {
console.log(this)
}
foo()
var bar = { woo: 1, foo: foo }
bar.boo()
var woo = bar.foo
woo()
```
```javascript=
// quiz
var foo = {
bar: function() {
console.log(this)
},
woo: {
lala: function() {
console.log(this)
}
}
}
foo.bar()
foo.woo.lala()
var foowoolala = foo.woo.lala
foowoolala()
```
#### ES6
* Default argument
```javascript=
const foo = (a, b = 1) => {
return a + b
}
foo(3)
```
* Template Expression
```javascript=
const name = "KK"
const foo = `Hi, this is ${name}.`
const bar = `Hi, they are ${names.map(name => name.toUpperCase()).join(', ')}.`
```
* Object Property Value Shorthand
```javascript=
const name = "KK"
const foo = {
name,
value: 123
}
```
* Spread syntax
```javascript=
// split
const foo = {a: 1, b: 2, c: 3}
const bar = [1, 2, 3]
const {a, b, ...restFoo} = foo // restFoo = {c: 3}
const [a, b, ...restBar] = bar // restBar = [3]
// merge
console.log({d: 4, e: 5, ...foo})
console.log([4, 5, ...bar])
```
```javascript=
// quiz
const foo = (arg) => {
const {a, b, ...rest} = arg
console.log(rest)
}
const foo = ({a, b, ...rest}) => {
console.log(rest)
}
foo({a: 1, b: 2, c: 3, d: 4})
const [, , , id] = url.split('/')
// const slices = url.split('/')
// const id = slices[3]
```
* import / export
```javascript=
import default_module from 'module-name'
import { submodule1, submodule2 } from 'module-name'
import * as no_defualt_module from 'module-name'
import * as AWS from 'aws-sdk'
import AWS from 'aws-sdk'
const s3 = new AWS.S3()
import * as Redux from 'redux'
import * as ApolloReact from 'apollo-react'
Redux.connect()
ApolloReact.connect()
```
```javascript=
export default module
export const submodule
export { submodule1, submodule2 }
```
```javascript=
// foo.js
import { bartender } from './bar'
// import * as bar from './bar' -> bar.bartender
import woo from './woo'
// bar.js
export const bartender = [1, 2, 3]
// woo.js
export default {a: 1, b: 2, c: 3}
```
* let / const
```javascript=
// var - function scope
// let/const - block scope
function() {
var a = 1
let b = 2
const c = 3
}
console.log(a, b, c)
if (true) {
var a = 1
let b = 2
const c = 3
}
console.log(a, b, c)
// const is for the memory addreess
const foo = []
foo.push(1) // foo -> [1]
foo[0] = 2
```
* Arrow Function
```javascript=
// 不會產生新的上下文
const foo = () => {
console.log(this)
}
const bar = {
foo,
woo: {
foo
}
}
foo()
bar.foo()
bar.woo.foo()
const foo = function() {
console.log(this)
const bar = () => {
console.log(this)
const woo = function() {
console.log(this)
}
}
}
```
* Class
```javascript=
class Person
class CookMan extends Person
new Person()
new CookMan()
const kk = {
name: "KK",
sayHi: () => console.log('Hi')
cook: () => console.log('lkk_cook')
}
const ken = {
name: "Ken",
sayHi: () => console.log('Hi~')
}
class ClassName extends SuperClassName {
variableName = 1;
constructor(args, extra_args) {
super(args)
}
methodA() {
console.log(this)
}
methodB = () => {
console.log(this)
}
methodC = function() {
console.log(this)
}
}
const instance = new ClassName("KK")
instance.variableName
instance.methodA()
instance.methodB()
instance.methodC()
class Dad {
money = 10
constructor(age) {
this.age = age
this.property = 10000000
}
taxi = () => {
// take taxi
}
}
class Son extends Dad {
constructor(property) {
super(6)
this.property = this.property > property ? this.property: property
}
}
const son = new Son(9999)
```
* Promise
> 敬請期待下次
### 組件概念

### 組件用法
```jsx=
<Component
key="xxx"
ref={ref => {}}
onEvent={event => {}}
style={{ marginTop: "10px" }}
custom="KK">
<ChildComponent />
<ChildComponent>
<GrandChildComponent />
</ChildComponent>
</Component>
// NOTE: Component 中間的組件會被作為 props.children 帶入 Component
<Component
key="xxx"
ref={ref => {}}
onEvent={event => {}}
style={{ marginTop: "10px" }}
custom="KK"
children={<>
<ChildComponent />
<ChildComponent>
<GrandChildComponent />
</ChildComponent>
</>}>
</Component>
$('#key').on('EventName', (event) => {
})
const handleClick = () => {
console.log('click')
}
<Button onClick={handleClick}>Foo</Button>
<Button onClick={handleClick()}>Foo</Button>
const res = handleClick()
<Button onClick={res}>Foo</Button>
```
### 組件類型
* Functional Component (aka PureComponent, Presentational Component)
```jsx=
const FunctionalComponent = (props) => {
const { name, children } = props
return (
<div>
<div>Hello, {name}!</div>
{children}
</div>
)
}
<FunctionalComponent name={name}>
<div>FC</div>
</FunctionalComponent>
<div id="xxx.ooo.yyy">
<div id="xxx.ooo.yyy.qqq">Hello, KK!</div>
<div id="xxx.ooo.yyy.www">FC</div>
</div>
```
* Class Component
```jsx=
class ClassComponent extends React.Component {
state = {
count: 0,
name: null,
item: {
name: null,
value: 0
}
}
handleHi = () => {
// this.setState({ count: this.state.count + 1 })
this.setState({ item: { value: 1 } })
// {
// count: 0,
// name: null,
// item: {
// value: 1
// }
// }
}
render() {
// const handleHiU = () => {
// this.setState({ count: this.state.count + 1 })
// }
return (
<div>
<div>Hello, {this.props.name}!</div>
<button onClick={this.handleHi}>Hi!</button>
<button onClick={() => {
console.log('hi')
}}>Hi!</button>
<div>{this.state.count}</div>
{this.props.children}
</div>
)
}
}
<ClassComponent name="KK">
<div>FC</div>
</ClassComponent>
```
> ReactDOM.render()
將 Virtual DOM 渲染到實際的 DOM 上面
```jsx=
ReactDOM.render(<App />, document.getElementById('root'))
```
> React.createElement()
此為 js 寫法,為了方便開發,故使用 `jsx` 語法糖
```javascript=
class Hello extends React.Component {
render() {
return React.createElement('div', null, `Hello ${this.props.toWhat}`)
}
}
ReactDOM.render(
React.createElement(Hello, {toWhat: 'World'}, null),
document.getElementById('root')
)
```
```jsx=
class Hello extends React.Component {
render() {
return <div>{`Hello ${this.props.toWhat}`}</div>
}
}
ReactDOM.render(
<Hello toWhat="World" />,
document.getElementById('root')
)
```
> component 可以是多個組件的集合,但是集合內的組件均需要給予 key
```jsx=
const ArrayComponent1 = (props) => {
const todos = ['eat', 'sleep', 'watch animate']
return todos.map(todo => <div key={todo}>{todo}</div>)
}
const ArrayComponent2 = (props) => {
const todos = ['eat', 'sleep', 'watch animate']
return todos.map((todo, idx) => <div key={idx}>{todo}</div>)
}
```
> 如果狀態需要更新陣列型態的話
```jsx=
// AddItem
this.setState({ items: [...this.state.items, newItem] })
// UpdateItem
this.setState({
items: [
...this.state.items.slice(0, idx),
updatedItem,
...this.state.items.slice(idx + 1),
]
})
// DeleteItem
this.setState({
items: [
...this.state.items.slice(0, idx),
...this.state.items.slice(idx + 1),
]
})
```
> Extend Custom Component
```jsx=
class Button extends React.Component {
}
class MainButton extends Button {
constructor({background, ...parentProps}) {
super(parentProps)
}
}
<Button onClick={}>Foo</Button>
<MainButton background="red" onClick={}>Foo</MainButton>
```
> 命名習慣
```jsx=
// props 名稱常以 onCallback 這個模式命名
const MyButton = ({ onClick, children }) => {
return <button onClick={onClick}>{children}</button>
}
// onCallback 內的處理函式常以 handleCallback 這個模式命名
const MyButton = ({ children }) => {
return <button onClick={handleClick}>{children}</button>
}
```
## 06/13(四)18:30 - 21:30
### 受控組件
```jsx=
class ControlledForm extends React.Component {
state = {
value1: '',
value2: '',
value3: '',
}
handleInput1Change = (e) => {
this.setState({value1: e.currentTarget.value})
}
handleInput2Change = (e) => {
this.setState({value2: e.currentTarget.value})
}
handleInput3Change = (e) => {
this.setState({value3: e.currentTarget.value})
}
render() {
return (
<form>
<input
value={this.state.value1}
onChange={this.handleInput1Change} />
<input
value={this.state.value2}
onChange={this.handleInput2Change} />
<input
value={this.state.value3}
onChange={this.handleInput3Change} />
</form>
)
}
```
### 不受控組件
```jsx=
class UncontrolledForm extends React.Component {
input1 = null
input2 = null
input3 = null
render() {
return (
<form>
<input ref={ref => this.input1 = ref} />
<input ref={ref => this.input2 = ref} />
<input ref={ref => this.input3 = ref} />
</form>
)
}
```
### Component Lifecycle

```jsx=
class ClassButton extends React.Component {
static getDerivedStateFromProps = (props, state) => {
// RARE
// use memorize or componentDidUpdate instead
// return null | new_state
}
shouldComponentUpdate = (nextProps, nextState) => {
// decide component to re-render or not
// return true | false
}
getSnapshotBeforeUpdate = (prevProps, prevState) => {
// RARE
// get the snapshot before updating the component
// ex. todo list, updating component will
// return null | snapshot
}
componentDidMount = () => {
// do something after mounting the component
// ex. fetch initial data
}
componentDidUpdate = (prevProps, prevState, snapshot) => {
// do something after updating the component
// ex. fetch another data
}
componentWillUnmount = () => {
// do something before unmounting the component
// ex. clear timer
}
render() {
return <button>Click me!</button>
}
}
```
### React Hook
#### State hook
```jsx=
// formula
const [state, setState] = useState(initialState)
```
```jsx=
const Button = () => {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}
```
```jsx=
// quiz
const ExplosionButton = (props) => {
// TODO: click button 10 times and explode!
return <button>還有 10 次爆炸!</button>
}
```
#### Effect hook
```jsx=
// formula
useEffect(() => {
// do something
// callback before unmounting
return () => {}
}, dependencies)
```
```jsx=
const Title = ({title}) => {
useEffect(() => {
const timer = setInterval(() => console.log(title), 1000)
return () => clearInterval(timer)
}, [])
return <div>{title}</div>
}
const Title1 = ({title}) => {
useEffect(() => {
const timer = setInterval(() => console.log(title), 1000)
return () => clearInterval(timer)
}, [title])
return <div>{title}</div>
}
const Table = ({ currentPage }) => {
useEffect(() => {
// fetch page
console.log(currentPage)
}, [currentPage])
}
```
```jsx=
// quiz
const Timer1 = () => {
const [count, setCount] = useState(0)
useEffect(() => {
const timer = setInterval(() => {
console.log(count + 1)
setCount(count + 1)
}, 1000)
return () => clearInterval(timer)
}, [count])
return <div>{count}</div>
}
const Timer2 = () => {
const [count, setCount] = useState(0)
useEffect(() => {
const timer = setInterval(() => {
console.log(count + 1)
setCount(count + 1)
}, 1000)
return () => clearInterval(timer)
}, [])
return <div>{count}</div>
}
```
### Custom hook
```jsx=
const Component = () => {
const [query, setQuery] = useQueryParams(window.location)
}
```
```jsx=
const useKK = (times) => {
const [left, setLeft] = useState(times)
useEffect(() => {
console.log(`KK left ${times} times.`)
}, [times])
return [left, () => setLeft(left - 1)]
}
const Button = () => {
const [times, useTimes] = useKK(100)
return <button onClick={() => useTimes()}>click me</button>
}
```
```jsx=
// quiz
const useTimer = (interval) => {
const [state, setState] = useState('stop')
const start = () => setState('start')
const pause = () => setState('pause')
const stop = () => setState('stop')
return [state, start, pause, stop]
}
const Player = () => {
const [timerState, onStart, onPause, onStop] = useTimer()
return <div>
<button onClick={onStart}>Play</button>
<button>Pause</button>
<button>Stop</button>
{timerState}
</div>
}
```
```jsx=
// homework
// fucking hard, think your best
const useInterval = (callback, delay) => {
const savedCallback = useRef();
// Remember the latest callback.
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
// Set up the interval.
useEffect(() => {
const tick = () => {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}
```
> reference [here](https://overreacted.io/making-setinterval-declarative-with-react-hooks/)
## 06/20(四)18:30 - 21:30
實際開發 Just Answer Now App
### 持續學習
* Higher-Order Component (HoC)
* React Context
* React Router
* Redux