React 开发基础
1.0 受控组件
1.1 父组件向子组件单向传值
1
2
3
|
renderSquare(i) {
return <Square value={i}/>;
}
|
子组件 Square
1
2
3
4
5
|
class Square extends React.Component {
render() {
return <button className="square">{this.props.value}</button>;
}
}
|
说明: 父组件使用属性方式传值过去, 子组件直接 props 接收
1.2 子组件向父组件传值
子组件通过 props 调用 父类的方法, 将数据通过方法参数传递过去
1
2
3
|
<button className="square" onClick={() => this.props.onClick.bind(this, this.state.value)}>
{this.props.value}
</button>
|
父组件, 调用子组件时传递函数
1
2
3
4
5
6
7
8
9
10
|
renderSquare(i) {
return (
<Square value={this.state.squares[i]} onClick={() => this.handleClick(i)} />
);
handleClick(i) {
const squares = this.state.squares.slice();
squares[i] = 'X';
this.setState({squares: squares});
}
|
注意在 handleClick
方法中调用了 slice
方法进行数组赋值, 为什么要这么做, 而不是在原数组中改变呢?
不直接在源数据上修改, 可以跟踪到数据的改变 , 同时确定了
1.2 函数组件
1
2
3
4
5
6
7
|
function Square(props) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}
|
1.2 如何阻止默认行为
1
2
3
4
5
6
7
8
9
10
11
12
|
function ActionLink() {
function handleClick(e) {
e.preventDefault(); // 阻止默认行为
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
|
1.3 函数事件绑定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = { isToggleOn: true };
// 为了在回调中使用 `this`,这个绑定是必不可少的
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return <button onClick={this.handleClick}>{this.state.isToggleOn ? 'ON' : 'OFF'}</button>;
}
}
|
通常情况下,如果你没有在方法后面添加 ()
,例如 onClick={this.handleClick}
,你应该为这个方法绑定 this
。
向事件处理程序传递参数, 两种方式等价
1
2
|
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button> // 显式传递
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button> // 隐式传递
|
1.4 使用条件运算符条件渲染
1.4.1 IF
1
2
3
4
5
6
7
|
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
|
1.4.2 &&
1
2
3
4
5
6
7
8
9
|
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 && <h2>You have {unreadMessages.length} unread messages.</h2>}
</div>
);
}
|
1.4.3 阻止渲染 return
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
|
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return <div className="warning">Warning!</div>;
}
class Page extends React.Component {
constructor(props) {
super(props);
this.state = { showWarning: true };
this.handleToggleClick = this.handleToggleClick.bind(this);
}
handleToggleClick() {
this.setState(state => ({
showWarning: !state.showWarning
}));
}
render() {
return (
<div>
<WarningBanner warn={this.state.showWarning} />
<button onClick={this.handleToggleClick}>{this.state.showWarning ? 'Hide' : 'Show'}</button>
</div>
);
}
}
|
1.5 生命周期
1
2
3
|
componentDidMount(){} // 渲染后
componentDidUpdate(){} // 更新时
componentWillUnmount(){} // 销毁时
|
1.6 列表
列表需要制定 key 值, key 唯一. 当不指定时默认以下标作为 key , 会出现警告提示
key 应在数组上下文中指定
1
2
3
4
5
6
7
8
9
10
11
12
13
|
function ListItem(props) {
// 正确!这里不需要指定 key:
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map(number => (
// 正确!key 应该在数组的上下文中被指定
<ListItem key={number.toString()} value={number} />
));
return <ul>{listItems}</ul>;
}
|
1.7 表单-受控组件
https://zh-hans.reactjs.org/docs/forms.html
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
|
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = { value: '' };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ value: event.target.value });
}
handleSubmit(event) {
alert('提交的名字: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
名字:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="提交" />
</form>
);
}
}
|
1.7.2 textarea
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
|
class EssayForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '请撰写一篇关于你喜欢的 DOM 元素的文章.'
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ value: event.target.value });
}
handleSubmit(event) {
alert('提交的文章: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
文章:
<textarea value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="提交" />
</form>
);
}
}
|
1.7.3 select
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
|
class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = { value: 'coconut' };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ value: event.target.value });
}
handleSubmit(event) {
alert('你喜欢的风味是: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
选择你喜欢的风味:
<select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">葡萄柚</option>
<option value="lime">酸橙</option>
<option value="coconut">椰子</option>
<option value="mango">芒果</option>
</select>
</label>
<input type="submit" value="提交" />
</form>
);
}
}
|
1.8 约定: 自定义组件以大写字母开头
react 认为小写的 tag 是原生 dom 节点
大写字母开头为自定义组件
1.9 生命周期
getDerivedStateFromProp
适合表单初始化
componentDidMount
适合资源加载, 发起 ajax 请求
componeWillUnmount
组件移除时调用, 释放资源
getSnapshotBeforeUpdate
render 之前调用, 获取 render 之前的状态
componentDidUpdate
ui 更新时被调用, 页面需要 prop 变化时重新获取数据
showldComponentUpdate
性能优化
1.9.1 demo 来了新消息保证位置不变
新数据会重新渲染界面, 就会导致高度不断增加, 滚动条根据消息变化, 在 render 之前获取高度, 更新时加上差值
2 HOOK
1
2
3
4
5
6
7
8
9
10
|
import React,{useState} from 'react';
const [count,setCount] = useState(0);
render(){
return (
<h2>{count} </h2>
<buttton onClick={ ()=>setCount(count+1) }> change</button>
);
}
|
ANT
1.0 安装
1
2
3
4
5
|
npm create umi
// 选择模板 app
// 空格选择功能,
npm install
npm run start
|
Typescript 语法
1.0 安装
1
|
sudo npm i typescript -g
|
1.3 函数事件绑定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = { isToggleOn: true };
// 为了在回调中使用 `this`,这个绑定是必不可少的
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return <button onClick={this.handleClick}>{this.state.isToggleOn ? 'ON' : 'OFF'}</button>;
}
}
|
通常情况下,如果你没有在方法后面添加 ()
,例如 onClick={this.handleClick}
,你应该为这个方法绑定 this
。
向事件处理程序传递参数, 两种方式等价
1
2
|
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button> // 显式传递
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button> // 隐式传递
|
1.4 使用条件运算符条件渲染
1.4.1 IF
1
2
3
4
5
6
7
|
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
|
1.4.2 &&
1
2
3
4
5
6
7
8
9
|
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 && <h2>You have {unreadMessages.length} unread messages.</h2>}
</div>
);
}
|
1.4.3 阻止渲染 return
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
|
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return <div className="warning">Warning!</div>;
}
class Page extends React.Component {
constructor(props) {
super(props);
this.state = { showWarning: true };
this.handleToggleClick = this.handleToggleClick.bind(this);
}
handleToggleClick() {
this.setState(state => ({
showWarning: !state.showWarning
}));
}
render() {
return (
<div>
<WarningBanner warn={this.state.showWarning} />
<button onClick={this.handleToggleClick}>{this.state.showWarning ? 'Hide' : 'Show'}</button>
</div>
);
}
}
|
1.5 生命周期
1
2
3
|
componentDidMount(){} // 渲染后
componentDidUpdate(){} // 更新时
componentWillUnmount(){} // 销毁时
|
1.6 列表
列表需要制定 key 值, key 唯一. 当不指定时默认以下标作为 key , 会出现警告提示
key 应在数组上下文中指定
1
2
3
4
5
6
7
8
9
10
11
12
13
|
function ListItem(props) {
// 正确!这里不需要指定 key:
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map(number => (
// 正确!key 应该在数组的上下文中被指定
<ListItem key={number.toString()} value={number} />
));
return <ul>{listItems}</ul>;
}
|
1.7 表单-受控组件
https://zh-hans.reactjs.org/docs/forms.html
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
|
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = { value: '' };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ value: event.target.value });
}
handleSubmit(event) {
alert('提交的名字: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
名字:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="提交" />
</form>
);
}
}
|
1.7.2 textarea
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
|
class EssayForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '请撰写一篇关于你喜欢的 DOM 元素的文章.'
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ value: event.target.value });
}
handleSubmit(event) {
alert('提交的文章: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
文章:
<textarea value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="提交" />
</form>
);
}
}
|
1.7.3 select
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
|
class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = { value: 'coconut' };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ value: event.target.value });
}
handleSubmit(event) {
alert('你喜欢的风味是: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
选择你喜欢的风味:
<select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">葡萄柚</option>
<option value="lime">酸橙</option>
<option value="coconut">椰子</option>
<option value="mango">芒果</option>
</select>
</label>
<input type="submit" value="提交" />
</form>
);
}
}
|
Redux 数据管理
state
数据
store
仓库
action
修改 state
reducer
触发 action 进行修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
// project > index.js
import { createStore } from 'redux';
const counterReducer = (state = 0, action) => {
switch (action.type) {
case 'INCREMENT':
return state + 1;
default:
return state;
}
};
const store = createStore(counterReducer);
console.log(store.getState());
// 指派动作
store.dispatch({
type: 'INCREMENT'
});
|
在 reducer 中不建议修改源数据, 使用以下方式复制数据
1
|
let newState = JSON.parse(JSON.stringify(state));
|
demo
1 创建 store 存放应用状态
1
2
3
4
5
6
7
8
9
10
11
|
// src -> store -> index.js
import { createStore } from 'redux';
import reducer from './reducer';
// 创建 Redux store 来存放应用的状态。
// API 是 { subscribe, dispatch, getState }。
const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
// 可以手动订阅更新,也可以事件绑定到视图层。
store.subscribe(() => console.log(store.getState()));
export default store;
|
2 创建 reducer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// src -> store -> reducer.js
import * as types from './action';
const defaultState = {
inputValue: 'Write Something',
list: ['早上 8 点晨会, 分配今天的任务1', '早上 8 点晨会, 分配今天的任务2', '早上 8 点晨会, 分配今天的任务3']
};
export default (state = defaultState, action) => {
switch (action.type) {
case types.CHANGEINPUT:
console.log('action', action);
console.log('state', state);
return Object.assign({}, state, { inputValue: action.value });
default:
return state;
}
};
|
3 创建 actionTypes
1
2
|
// src -> store -> actionTypes.js
export const CHANGEINPUT = 'CHANGEINPUT';
|
4 创建 actionCreates.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import * as types from './actionTypes';
export const ChangeInputAction = val => ({
type: types.CHANGE_INPUT,
val
});
export const AddListTile = () => ({
type: types.ADD_LIST_TILE
});
export const DeleteItem = index => ({
type: types.DELETE_ITEM,
index
});
|
组件 ui 与业务逻辑拆分
创建业务逻辑与组件 ui, 使用 业务逻辑页面调用 组件
使用无状态组件有更好的性能, 无状态组件存放 ui
异步请求
1
2
3
4
5
6
7
|
yarn add axios
import axios from 'axios'
axios.get('').then(()=>{
console.log
})
|