Redux

Work flow

Redux Basis

React Redux Basis

Work flow of redux

redux work flow

Redux Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import {createStore, combineReducers, applyMiddleware} from 'redux'
import ReduxThunk from 'redux-thunk'
import { composeWithDevTools} from 'redux-devtools-extension'

const initialState = {
count: 0
}

const userInitialState = {
username: 'john'
}



const ADD = 'ADD'
function counterReducer(state = initialState, action) {
console.log(state, action)
switch (action.type) {
case ADD:
return {count: state.count + (action.num || 1) }
default:
return state
}
}
const UPDATE_NAME = 'UPDATE_NAME'
function userReducer(state= userInitialState, action) {
switch(action.type) {
case UPDATE_NAME:
return {...state, username: action.name}
default:
return state
}
}

const allReducers = combineReducers({
counter: counterReducer,
user: userReducer
})
// createStore(reducer, [preloadedState], [enhancer])
const store = createStore(allReducers,
{
counter: initialState,
user: userInitialState
},
composeWithDevTools(applyMiddleware(ReduxThunk)) //执行异步dispatch,异步action, 和一个有效的redux调试工具
);

// action create
function add(num) {
return {
type: ADD,
num,
}
}

//
function addAsync(num) {
return (dispatch, getState) => setTimeout(()=>{
dispatch(add(num))
}, 1000)
}

console.log(store.getState())
store.dispatch({type: ADD})
console.log(store.getState())

store.subscribe(()=>{
console.log('changed', store.getState())
})

store.dispatch(add(3))
// store.dispatch({type: ADD})
store.dispatch(addAsync(5))
store.dispatch({type: UPDATE_NAME, name: 'Yehya'})

export default store

React Redux

in _app.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import App, {Container} from 'next/app'
import { Provider } from 'react-redux'

import 'antd/dist/antd.css'
import Layout from "../components/Layout";
import '../styles.css'
import MyContext from '../lib/my-context'
import store from '../store/store'

class MyApp extends App {
state = {
context: 'value'
}
static async getInitialProps({Component, ctx}) {
console.log('init app')
let pageProps = {}
if(Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx)

}
return {
pageProps
}

}

render() {
const {Component, pageProps} = this.props//每个页面渲染的时候都会作为Component
return (
<Container>
<Layout>
<Provider store={store}>
<MyContext.Provider value={this.state.context}>
<Component {...pageProps}/>
<button onClick={()=>this.setState({context:`${this.state.context}111`})}>update context</button>
</MyContext.Provider>
</Provider>
</Layout>
</Container>
)
}
}

export default MyApp

we import store.

In index.js

1
2
3
4
5
6
7
8
9
10
11
export default connect(function mapStateToProps(state) {
return {
counter: state.counter.count,//exposed by createStore
username: state.user.username
}
}, function mapDispatchToProps(dispatch) {
return {
add: (num)=> dispatch({ type: 'ADD', num}),
rename: (name) => dispatch({type: 'UPDATE_NAME', name})
}
})(Index)

store创建,connect 用法, redux实现flux数据流向 通过点击button 触发 action,action调用reducer,更新state,传递给provider,进入react

Currying

How to implement an add funtion?

1
2
3
4
function add(x, y) {
return x+y;
}
add(1, 3)===4//true

How to implement add(1)(3) === 4?

1
2
3
4
5
function add(x) {
return function(y) {
return x+y;
}
}

How to implement add3(2) === 5?

1
2
3
4
5
6
7
8
function currying(fn, ...args1) {
return function(...args2) {
fn(...args1, ...args1);
}
}

var increment = currying(add, 1);
increment(5)===6//true

How to implement infinite arguments?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function trueCurrying(fn, ...args1) {
if(args1.length>=fn.length) return fn(...args1);
return function(...args2) {
return trueCurrying(fn, ...args1, ...args2);
}
}

function triAdd(x, y, z) = {
return x+y+z;
}

var curryAdd = trueCurrying(triAdd);
curryAdd(1,2,3)===6//true;
curryAdd(1,2)(3)===6//true;
curryAdd(1)(2)(3)===6//true;

React Hook基本总结

State

引入方式

1
import React, {useState, useReducer} from 'react'

useState声明方式

1
const [state, setState] = useState(initialState);

useState返回后的第一个值将始终是更新后的最新的state

setState修改状态方法

1
2
3
4
5
6
7
8
9
10
11
function Counter({initialCount}) {
const [count, setCount] = useState(initialCount);
return (
<>
Count: {count}
<button onClick={() => setCount(initialCount)}>Reset</button>
<button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
</>
);
}

useReducer声明方式

1
const [state, dispatch] = useReducer(reducer, initialArg, init);

e.g.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const initialState = {count: 0};

function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}

function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}

Effect

useEffect

1
2
3
4
5
useEffect(() =>{
console.log('effect invoked')//didmount
return () => console.log('effect detected')//willUnmount
}, [count]//only when count changes, rebuild
);

useLayoutEffect

  • 函数签名与useEffect相同
  • 会在任何state更新后,计算新DOM节点数,还未更新到真实DOM页面时执行
  • 执行顺序
1
2
3
4
5
6
Layout effect invoked
b_old.js:65 effect invoked
b_old.js:74 layout effect detected
b_old.js:73 Layout effect invoked
b_old.js:66 effect detected
b_old.js:65 effect invoked

Context

Context is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language.

React.createContext() && useContext()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};

const ThemeContext = React.createContext(themes.light);

function App() {
return (
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
);
}

function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}

function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background, color: theme.foreground }}>
I am styled by theme context!
</button>
);
}

useRef

  • 获取某一个DOM节点,或组件的实例
  • ref过去在functional component没有,因为无this,无法挂载
  • 返回同一个对象,不会生成新的对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function MyCountFunc() {
// const[count, setCount] = useState(0)//[a, b]

const [count, dispatchCount] = useReducer(countReducer, 0)
const [name, setName] = useState('John')

const inputRef = useRef()
useEffect(()=>{
console.log(inputRef)
return () =>console.log('effect detected')//willUnmount

}, [count])//dependency数组中的项,如果未变化,则不会执行effect,且不会卸载effect

//会在任何state更新后,计算新DOM的节点数,还未更新到真实dom页面,执行
//layout实际使用比较少,如果执行了很多需要长时间运行的代码,会导致渲染时间长,页面卡住
useLayoutEffect(()=>{
console.log('Layout effect invoked')//didmount
return () =>console.log('layout effect detected')//willUnmount
}, [count])//dependency数组中的项,如果未变化,则不会执行effect,且不会卸载effect

// return <span>{count}</span>
return (
<div>
<input ref={inputRef} value={name} onChange={(e)=>setName(e.target.value)}/>
<button onClick={()=>dispatchCount({type: 'add'})}>{count}</button>
<p>{Context}</p>
</div>
)
}

Hooks Render Optimization

  • memo
  • useMemo: 普遍用于对函数中参数的优化
  • useCallback: 普遍用于对函数的优化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
function MyCountFunc() {
const [count, dispatchCount] = useReducer(countReducer, 0);
const [name, setName] = useState('John')

const countRef = useRef()//返回同一个对象,不会生成新的对象
countRef.current = count

const config = useMemo(()=>({
text: `count is ${count}`,
color: count>3? 'red': 'blue'
})
, [count])

const handleButtonClick = useCallback(() => dispatchCount({type: 'add'}), [dispatchCount])

const handleAlertButtonClick = function() {
setTimeout(()=>{
alert(countRef.current)
}, 2000)
}

return (
<div>
<input value={name} onChange={e=>setName(e.target.value)}/>
<Child
config = {config}
onButtonClick={handleButtonClick}
/>
<button onClick={handleAlertButtonClick}>alert count</button>
</div>
)
}

const Child = memo(
function Child( {onButtonClick, config}) {
console.log('child render');
return (
<button onClick={onButtonClick} style={{color: config.color}}>
{config.text}
</button>
)
}
)

Closure Trap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function MyCountFunc() {
const [count, dispatchCount] = useReducer(countReducer, 0);
const [name, setName] = useState('John')

const countRef = useRef()//返回同一个对象,不会生成新的对象
countRef.current = count

const config = useMemo(()=>({
text: `count is ${count}`,
color: count>3? 'red': 'blue'
})
, [count])

const handleButtonClick = useCallback(() => dispatchCount({type: 'add'}), [dispatchCount])

const handleAlertButtonClick = function() {
setTimeout(()=>{
alert(countRef.current)
}, 2000)
}

return (
<div>
<input value={name} onChange={e=>setName(e.target.value)}/>
<Child
config = {config}
onButtonClick={handleButtonClick}
/>
<button onClick={handleAlertButtonClick}>alert count</button>
</div>
)
}

在调用setTimeout函数时,如果将count作为参数,则输出的是调用该函数时的count对象。如果想获取即时的值,则使用useRef

Hello World

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment