官方文档入门

初步体会一个react的小应用


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//利用组件定义标签
class ShoppingList extends React.Component {
render() {
return (
<div className="shopping-list">
<h1>Shopping List for {this.props.name}</h1>
<ul>
<li>Instagram</li>
<li>WhatsApp</li>
<li>Oculus</li>
</ul>
</div>
);
}
}

// Example usage: <ShoppingList name="Mark" />

JSX语法:js内嵌html。


组件之间传值:通过props


基本流程:就是继承react组件基类,自定义组件及其行为。实例化之后填入html某一个标签中。


组件内部结构:

  1. 构造函数(如果要维护内部的state需要有,否则可删除)
  2. 内部自定义函数
  3. render函数
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

class Game extends React.Component {
constructor(props) {
super(props);
this.state = {
history: [{
squares: Array(9).fill(null),
}],
xIsNext: true,
};
}
render() {
const history = this.state.history;
const current = history[history.length - 1];
const winner = calculateWinner(current.squares);
let status;
if (winner) {
status = 'Winner: ' + winner;
} else {
status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
}
return (
<div className="game">
<div className="game-board">
<Board squares={current.squares}
onClick={(i) => this.handleClick(i)}/>
</div>
<div className="game-info">
<div>{status}</div>
<ol>{/* TODO */}</ol>
</div>
</div>
);
}
}

// ========================================
// 调用函数进行渲染
ReactDOM.render(
<Game />,
document.getElementById('root')
);
//

组件结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class  example extends React.Componet{
construct(props){
super(props);
this.state = { // state 存储本组件的变量成员
变量: 值, // props 上层组件调用的传入的变量
};
}
// 其他处理的函数
func(){

}

// 最后的渲染函数,返回html元素
render(){
// 一些变量处理逻辑
return(<div> <div/>);
}
}
ReactDOM.render(
<example />, // 此处将组件作为标签渲染
document.getElementById('root') // 查找root 标签,将组件填入
);

慕课网视频

生命周期

上述是一些hook函数,可以以下面的方式调用

绑定事件

定义事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var test = React.createClass({
change: function(event){
var tipE = React.findDOMNode("tip");
if(tip.display == 'none')
tip.display = 'inline';
else
tip.display = 'none';
event.stopPropagation();
evnet.preventDefault();
}

render: function(){
return (
<button onClick = {this.change}> click <button/><span ref = "tip"> <span/>
);
}
});

React 项目

  • yomen使用学习
  • 体会网页版的todoapp

https://yeoman.io/codelab/review-generated-files.html

https://www.jianshu.com/p/8fcf8d27b9fe

  • 多人投票项目,待办
  • MERN 全栈开发 pdf ,待办。

Redux 学习

https://redux-docs.netlify.com/introduction/learning-resources

官方介绍和推荐学习资源 !

http://cn.redux.js.org/ 中文文档

action 组件可能的行为,需要先定义分类type,然后再生成行为createaction


reducer 根据当前状态行为确定下一步状态,(previousState, action) => newState

It’s very important that the reducer stays pure. Things you should never do inside a reducer:

  • Mutate its arguments;
  • Perform side effects like API calls and routing transitions;
  • Call non-pure functions, e.g. Date.now() or Math.random()

container将redux和react组件绑定在一起


store 维持应用状态

官方代码学习:


一个简单的计时器应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 入口文件 index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore } from 'redux'
import Counter from './components/Counter'
import counter from './reducers'

const store = createStore(counter) // 获取存储
const rootEl = document.getElementById('root')

const render = () => ReactDOM.render(
<Counter
value={store.getState()} // 将store中的state值传入组件的值
onIncrement={() => store.dispatch({ type: 'INCREMENT' })} //为组件注册store中的方法
onDecrement={() => store.dispatch({ type: 'DECREMENT' })}
/>,
rootEl
)

render() // 渲染
store.subscribe(render) // 为渲染结果进行stroe的订阅
1
2
3
4
5
6
7
8
9
10
11
// reducer 文件,根据传入的行为定义响应结果
export default (state = 0, action) => {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
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
// 定义时钟组件
import React, { Component } from 'react'
import PropTypes from 'prop-types'

class Counter extends Component {
constructor(props) {
super(props);
this.incrementAsync = this.incrementAsync.bind(this);
this.incrementIfOdd = this.incrementIfOdd.bind(this);
}

incrementIfOdd() {
if (this.props.value % 2 !== 0) {
this.props.onIncrement()
}
}

incrementAsync() {
setTimeout(this.props.onIncrement, 1000)
}

render() {
const { value, onIncrement, onDecrement } = this.props //获取上层调用的参数
return (
<p>
Clicked: {value} times // 将上层传来的值绑定在组件的属性之上
{' '}
<button onClick={onIncrement}> // 将上层定义的事件绑定给下层组件
+
</button>
{' '}
<button onClick={onDecrement}>
-
</button>
{' '}
<button onClick={this.incrementIfOdd}>
Increment if odd
</button>
{' '}
<button onClick={this.incrementAsync}>
Increment async
</button>
</p>
)
}
}

Counter.propTypes = { // 组件的属性
value: PropTypes.number.isRequired,
onIncrement: PropTypes.func.isRequired,
onDecrement: PropTypes.func.isRequired
}

export default Counter

Todos 简单应用学习

  • react-redux中Provider的概念,相当于一个顶层的组件,直接存储了store。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 入口文件
import React from 'react' //
import { render } from 'react-dom' // render 对象
import { createStore } from 'redux' // 从redux引入createStore方法创建store
import { Provider } from 'react-redux' // 引入顶层组件,注册store
import App from './components/App' // 引入App组件,核心组件
import rootReducer from './reducers' // 一次性引入多个reducer

const store = createStore(rootReducer)

render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
  • 学习reducer 如何向其他reducer托管处理行为的逻辑。

reducer中,index.js中combineReducers为汇总的reducer。

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
// reducers/index.js
import { combineReducers } from 'redux'
import todos from './todos'
import visibilityFilter from './visibilityFilter'

export default combineReducers({ //联合其他reducer
todos,
visibilityFilter
})

///////////////////////////////////////////////////////////////

// /reducers/todos.js ??
const todos = (state = [], action) => {
switch (action.type) {
case 'ADD_TODO': // 添加一条todo
return [
...state,
{
id: action.id,
text: action.text,
completed: false
}
]
case 'TOGGLE_TODO': // 完成todo
return state.map(todo =>
(todo.id === action.id)
? {...todo, completed: !todo.completed}
: todo
)
default:
return state
}
}

export default todos

////////////////////////////////////////////////////////////////////////

// reducers/visibilityFillter.js
import { VisibilityFilters } from '../actions' // 将行为交给其他模块

const visibilityFilter = (state = VisibilityFilters.SHOW_ALL, action) => {
switch (action.type) {
case 'SET_VISIBILITY_FILTER': // 设置可视化条件
return action.filter
default:
return state
}
}

export default visibilityFilter

定义行为类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 可以把type和 actioncreator分开

let nextTodoId = 0
export const addTodo = text => ({
type: 'ADD_TODO',
id: nextTodoId++,
text
}) // 添加todo 行为

export const setVisibilityFilter = filter => ({
type: 'SET_VISIBILITY_FILTER',
filter
}) // 设置可视化条件,点击active 按钮或者completed

export const toggleTodo = id => ({
type: 'TOGGLE_TODO',
id
}) // 删除待办

export const VisibilityFilters = {
SHOW_ALL: 'SHOW_ALL',
SHOW_COMPLETED: 'SHOW_COMPLETED',
SHOW_ACTIVE: 'SHOW_ACTIVE'
} // 可视化

表面组件和容器组件

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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// 三个组件
import React from 'react'
import Footer from './Footer'
import AddTodo from '../containers/AddTodo'
import VisibleTodoList from '../containers/VisibleTodoList'

const App = () => (
<div>
<AddTodo />
<VisibleTodoList />
<Footer />
</div>
)

export default App

//////////////////////////////////////////////////////////////////////

// AddTodo.js
// 这个逻辑比较简单,分发add todo 事件即可
import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'

const AddTodo = ({ dispatch }) => {
let input

return (
<div>
<form onSubmit={e => {
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(addTodo(input.value)) // 分发addtodo行为,在action中定义好了
input.value = ''
}}>
<input ref={node => input = node} />
<button type="submit">
Add Todo
</button>
</form>
</div>
)
}

export default connect()(AddTodo) // 将react组件使用connect建立起redux和react之间的联系

/////////////////////////////////////////////////////////////////////////

// FilterLink.js
import { connect } from 'react-redux'
import { setVisibilityFilter } from '../actions'
import Link from '../components/Link'

const mapStateToProps = (state, ownProps) => ({
active: ownProps.filter === state.visibilityFilter
}) // Link组件的属性active,通过比较组件当前状态和上级传入状态,确定是否隐藏

const mapDispatchToProps = (dispatch, ownProps) => ({
onClick: () => dispatch(setVisibilityFilter(ownProps.filter))
}) // link组件的点击事件,根据分发事件来确定

export default connect(
mapStateToProps,
mapDispatchToProps
)(Link) // 将两个事件处理绑定在link组件中

///// 组件之中无需注意事件分发逻辑,只需要暴露组件事件分发的接口
import React from 'react'
import PropTypes from 'prop-types'

const Link = ({ active, children, onClick }) => (
<button
onClick={onClick}
disabled={active}
style={{
marginLeft: '4px',
}}
>
{children}
</button>
)

Link.propTypes = {
active: PropTypes.bool.isRequired,
children: PropTypes.node.isRequired,
onClick: PropTypes.func.isRequired
}

export default Link

///////////////////////////////////////////////////////////////////////////

// visibletodolist.js
import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import TodoList from '../components/TodoList'
import { VisibilityFilters } from '../actions'

// 根据fiter 确定reducer todos如何确定操作
const getVisibleTodos = (todos, filter) => {
switch (filter) { // fiter是state.visibilityFilter,一个reducer,会根据分发的action确定返回action.filter,即是显示active还是显示completed,然后todos也是一个reducer将会根据显示条件,对todo进行分类,
case VisibilityFilters.SHOW_ALL:
return todos
case VisibilityFilters.SHOW_COMPLETED:
return todos.filter(t => t.completed)
case VisibilityFilters.SHOW_ACTIVE:
return todos.filter(t => !t.completed)
default:
throw new Error('Unknown filter: ' + filter)
}
}

const mapStateToProps = state => ({ // 得到状态,显示todo
todos: getVisibleTodos(state.todos, state.visibilityFilter) // state代表一个全局顶层的reducer
}) // state.todos 代表一个reducer,第二个参数也是,返回一个todo的显示与否的结果

const mapDispatchToProps = dispatch => ({ // 分发事件的行为,点击待办,删除
toggleTodo: id => dispatch(toggleTodo(id)) // 按id区分行为
})

export default connect(
mapStateToProps,
mapDispatchToProps
)(TodoList) // 对组件todolist todos和toggleTodo属性进行状态和分发事件的绑定


////////////////////////// todolist 事件
import React from 'react'
import PropTypes from 'prop-types'
import Todo from './Todo'

const TodoList = ({ todos, toggleTodo }) => (
<ul>
{todos.map(todo =>
<Todo
key={todo.id}
{...todo} // 按照id来分配是否toggletodo事件
onClick={() => toggleTodo(todo.id)} // 先确定行为属于toggleTodo,然后reducer解决处理,确定todo的completed的值,todo.id ,todo代表状态todos中的一条todo,todo.id为todo的序号,传入id后将会对该todo的completed取反,结果会导致本来是横线会变无横线,或者无横线变有横线
/>
)}
</ul>
)

TodoList.propTypes = {
todos: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.number.isRequired,
completed: PropTypes.bool.isRequired,
text: PropTypes.string.isRequired
}).isRequired).isRequired,
toggleTodo: PropTypes.func.isRequired
}

export default TodoList

/// todo.js
import React from 'react'
import PropTypes from 'prop-types'

// 单条todo的三个属性
const Todo = ({ onClick, completed, text }) => (
<li
onClick={onClick}
style={{ // 属性与上层todolist绑定
textDecoration: completed ? 'line-through' : 'none'
}}
>
{text}
</li>
)

Todo.propTypes = {
onClick: PropTypes.func.isRequired,
completed: PropTypes.bool.isRequired,
text: PropTypes.string.isRequired
}

export default Todo

todos-with-undo

使用redux undo实现用更少的代码撤销或者回退上一步(取消撤销)

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
// 定义好todos 这一个reducer,然后下面这句,最后export undoabletodos
const undoableTodos = undoable(todos, { filter: includeAction(['ADD_TODO', 'TOGGLE_TODO']) })


// 在redo 和undo按钮中,注册redo
import React from 'react'
import { ActionCreators as UndoActionCreators } from 'redux-undo'
import { connect } from 'react-redux'

let UndoRedo = ({ canUndo, canRedo, onUndo, onRedo }) => (
<p>
<button onClick={onUndo} disabled={!canUndo}>
Undo
</button>
<button onClick={onRedo} disabled={!canRedo}>
Redo
</button>
</p>
)

const mapStateToProps = (state) => ({
canUndo: state.todos.past.length > 0, //state.todos相当于undoableTodos
canRedo: state.todos.future.length > 0
})

const mapDispatchToProps = ({
onUndo: UndoActionCreators.undo, // 这一步是 考虑组件变化记忆???
onRedo: UndoActionCreators.redo
})

UndoRedo = connect(
mapStateToProps,
mapDispatchToProps
)(UndoRedo)

export default UndoRedo

redux中数据的生命周期主要是以下四个步骤:

  1. 你调用事件分发 store.dispatch(action)
  2. redux 的store调用给定好的reducer函数
  3. root reducer将多个reducers函数的输出值装入一个单一的状态树