react-day2

react 学习笔记day-02

处理事件

使用React元素处理事件与处理DOM元素上的事件非常相似,有一些语法差异
React事件使用camelCase命名,而不是小写
使用JSX,将传递函数作为事件处理函数,而不是字符串

HTML:

<button onclick = "activateLasers()">Activate Lasers</button>

React:

<button onClick={activateLasers}>Activate Lasers</button>

另外一个区别是,无法返回false以防React的默认行为,必须使用preventDefault明确调用。

HTML:

<a href="#" onclick="conslog.log('The link was clicked'); return false">Click me</a>

React:

1
2
3
4
5
6
7
8
9
10
11
function ActionLink(){
function handleClick(e){
e.preventDefault();
console.log('The link was clicked.');
}
return(
<a href="#" onClick={handleClick}>
Click me
</a>
);
}

使用React,通常不需要调用addEventListener添加侦听器到DOM元素后创建,相反,只需在元素初始呈现时提供侦听器
Toggle组件

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
class Toggle extends React.Component{
constructor(props){
super(props);
this.state = {isTogglenOn:true};

this.handleClick = this.handleClick.bind(this);
}
handleClick(){
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render(){
return(
<button onClick={this.handleClick}>
{this.state.isToggleOn?'ON'?'OFF'}
</button>
);
}
}

React.render(
<Toggle/>,
document.getElementById('root');
);

如果引用一个没有()后面的方法,比如onClick={this.handleClick}你应该绑定那个方法

1
2
3
4
5
6
7
8
9
10
11
12
class LoggingButton extends React.component{
handleClick = () =>{
console.log('this is ',this);
}
render(){
return(
<button onclick={this.handleClick}>
Click me
</button>
)
}
}

将参数传递给事件处理程序

在循环内部,通常需要将一个额外的参数传递给事件处理程序。下例,id是行ID

<button onClick={(e) => this.deleteRow(id,e)}>Delete Row</button>
<button onClcik={this.deleteRow.bind(this,id)}>Delete Row</button>

e代表React事件的参数都将作为ID之后的第二个参数传递,使用箭头函数,我们必须明确传递它,但是bind任何更多的参数都会自动转发

有条件的渲染

React中,可以创建封装所需行为的独特组件,可以仅渲染其中的一部分,具体取决于应用程序的状态
React中的条件呈现与条件跟js的工作方式相同,使用js的if运算符,或者是条件运算符创建表示当前状态的元素,并让React更新UI来匹配它们

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function UserGreeting(props){
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props){
return <h1>Please sign up.</h1>
}

function Greeting(props){
const isLoggedIn = props.isLoggedIn;
if(isLoggedIn){
return <UserGreeting/>;
}
return <GuserGreeting/>;
}
React.render(
<GuestGreeting isLoggedIn={false}/>,
documenet.getElementById('root');
);

元素变量

可以使用变量来存储元素,有条件地渲染组件的一部分,而其余输出不会更改

代表注销和登录按钮的新组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function LoginButton(props){
return(
<button onClick = {props.onClick}>
Login
</button>
);
}
function LogoutButton(props){
return(
<button onClick = {props.onClick}>
Logout
</button>
);
}

创建有状态的组件LoginControl,呈现<LoginButton/><LogoutButton/>根据其当前状态,它也会呈现一个<Greeting/>

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
class LoginControl extends React.Component{

constructor(props){
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn : false};
}

handleLoginClick(){
this.setState({isLoggedIn:true});
}

handleLogoutClick({
this.setState({isLoggedIn:false});
});

render(){
const isLoggedIn = this.state.isLoggedIn;
let button = null;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return(
<div>
<Greeting isLoggedIn = {isLoggedIn} />
{button}
</div>
);
}
}

function UserGreeting(props){
return <h1>Welcome back!</h1>
}

function GuestGreeting(props){
return <h1>Please sign up.</h1>
}

function Greeting(props){
const isLoggedIn = props.isLoggedIn;
if(isLoggedIn){
return <UserGreeting />;
}
return <GusetGreeting />;
}

function LoginButton(props){
return(
<button onClick = {props.onClick}>
Login
</button>
);
}

function LogoutButton(){
return(
<button onClick = {props.onClick}>
Logout
</button>
)
}

ReactDOM.render(
<LoginControl />,
document.getElementById('root');
);

内联使用逻辑&&运算符

包裹在花括号中嵌入JSX的任何表达式中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Mailbox(props){
const unreadMessage = props.unreadMessage;
return(
<div>
<h1>Hello</h1>
{unreadMessage.length > 0 &&
<h2> You have {unreadMessage.length } unread message</h2>
}
</div>
);
}

const message = ['React','Re:React','Re:Re:React'];
ReactDOM.render(
<Mail unreadMessage = {message} />,
document.getElementById('root');
);

与条件元素内联使用if-else

eg1:

1
2
3
4
5
6
7
8
render(){
const isloggedIn = this.state.isLoggedIn;
return(
<div>
The user is <b> {isLoggIn ? 'currently' : 'not'}</b> logged in
</div>
);
}

eg2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
render(){
const isLoggedIn = this.state.isLoggedIn;
return(
<div>
{
isLoggedIn ? (
<LogoutButton onClick = {this.handleLogoutClick /}>
):(
<LoginButton onClick = {this.handleLoginClick} />
)
}
</div>
);
}

防止组件呈现

组件可以隐藏自身,null

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
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(prevState =>({
showWarning : !prevState.showWarning
}));
}

render(){
return(
<div>
<WarningBanner warn = {this.state.showWarning} />
<button onClick = {this.handleToggleClick}>
{this.state.showWarning ? 'Hide': 'Show'}
</button>
</div>
);
}

ReactDOM.render(
<Page/>,
document.getElementById('root')
);

null从组件的render方法返回不会影响声明周期方法的触发

列表和键

js中转换列表,map函数来获取numbers并将其值加倍

1
2
3
const numbers = [1,2,3,4,5];
const doubled = numbers.map((number) => number*2);
console.log(doubled);

而在React中,将数组转换为元素列表几乎完全一样

渲染多个组件

可以构建元素集合,并使用大括号将其包含在JSX中 {}
numbers使用js map函数遍历数组,li为每个项目返回一个元素,最后将得到的元素数组分配给listItems

1
2
const numbers = [1,2,3,4,5];
const listItems = number.map((number) => <li>{number}</li>);

再将整个listItems数组包含在一个ul元素中,并将其呈现给DOM

1
2
3
4
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
);

基本列表组件

组件中渲染列表
可以将前面的例子重构为一个组件,该组件接受一个数组numbers并输出一个无序的元素列表

1
2
3
4
5
6
7
8
9
10
11
12
13
function NumberList(props){
const number = props.number;
const listItems = number.map((number) => <li>{number}</li>);
return(
<ul> {listItems} </ul>
);
}

const number = []1,2,3,4,5];
ReactDOM.render(
<NumberList numbers = {numbers} />,
document.getElementById('root')
);

运行这段代码时,会给你一个警告,即应该为列表项提供一个密钥。“键”是在创建元素列表时需要包含的特殊字符串属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function NumberList(props){
const numbers = props.numbers;
const listItems = number.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return(
<ul>{listItems}</ul>
);
}
const numbers = [1,2,3,4,5];
ReactDOM.render(
<NumberList number = {numbers} />,
document.getElementById('root')
);

可以帮助React识别哪些项目已经更改,添加,删除。键应该赋予数组内稳定的元素

1
2
3
4
5
6
const numbers - [1,2,3,4,5];
const listItems = number.map((number) =>
<li key = {number.toString()}>
{number}
</li>
);

选择id作为标志关键字比较多

1
2
3
4
5
const todoItems = todo.map(
(todo) => <li key = {todo.id}>
{todo.text}
</li>
);

如果没有稳定id,可以使用索引

1
2
3
4
5
const todoItems = todos.map((todo,index) =>
<li key = {index}>
{todo.text}
</li>
);

如果项目的顺序会改变的话,则不建议使用索引,这可能会对性能造成负面影响,并可能带来组件状态问题。

用键提取组件

提取listItem组件,保留数组中的元素而不是本身li元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function ListItem(props){
return <li>{props.value}</li>
}
function NumberList(props){
const numbers = props.number;
const listItems = numbers.map((number) =>
<ListItem key = {number.toString()} value = {number} />
);
return(
<ul>{listItems}</ul>
);
}
const numbers = [1,2,3,4,5];
ReactDOM.render(
<NumberList numbers = {numbers} />,
document.getElementById('root')
);

key在兄弟姐妹中必须是唯一的
在我们生成两个不同数组的时候,我们可以用相同的键

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
function Blog(props){
const sidebar = (
<ul>
{props.posts.map(post) =>{
<li key = {post.id}>{post.title}</li>
}}
</ul>
);
const content = props.posts.map((post) =>
<div key = {post.id}>
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
);
return (
<div>
{sidebar}
<hr/>
{content}
</div>
);
}

const posts = [{id;1,title:'title1',content:'content1'},{id;2,title:'title2',content:'content2'}];
ReactDOM.render(
<Blog posts = {posts}>,
document.getElementById('root')
);

在JSX嵌入map()

1
2
3
4
5
6
7
8
9
10
function NumberList(props){  
const numbers = props.number;
return(
<ul>
{number.map(number) =>
<ListItem key = {number.toString()} value = {number} />
}
</ul>
);
}

这样嵌套使用有可能提高了可读性,但是如果map嵌套起来太复杂的话就可以考虑提取组件出来

形式forms

受控组件

表单元素(Input/textarea/select)通常保持自己的状态并根据用户输入更新,在React中,可变状态通常保存在组件的状态属性中,并且更新只能使用setState()
可以通过使React状态成为单一真相源来结合着两者,然后呈现表单的React组件也控制后续用户输入中以该表单发生的事情。输入表单元素的值由React以这种方式控制,叫做‘受控组件’

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
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.targer.value});
}

handleSubmit(event){
alert('提交的值为: ' +this.state.value);
event.preventDefault();
}

render(){
return(
<form onSubmit = {this.handleSubmit}>
<label>
Name:
<input type="text" value = {this.state.value} onChange = {this.handleChange} />
</label>
<input type="submit" value="Submit"/>
</form>
);
}

ReactDOM.render(){
<NameForm />,
document.getElementById('root')
}

由于value属性设置在表单元素上,因此显示的值将始终为this.state.value,使React状态成为真值的来源,由于handleChange每次击键时都会运行以更新React状态,因此显示的值将随用户键入而更新

对于受控组件,每个状态变异都会有一个关联的处理函数,这使修改或验证用户输入变得非常简单,例如我们想强制全部大写来编写名称

1
2
3
handleChange(event){
this.setState({value:event.target.value.toUpperCase()});
}

textarea标签

HTML中:

<textarea>textarea</textarea>

React中,value属性代表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
33
34
35
36
37
class EssayForm extends React.Component{

construct(props){
super(props);
this.state = {
value:'textarea 123321'
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}

handleChange(){
this.setState({value:event.target.value});
}

handleSubmit(){
alert('new textarea content is' +this.state);
event.preventDefault();
}

render(){
return(
<form onSubmit = {this.handleSubmit}>
<label>
textarea:
<textarea value = {this.state.value} onChange = {this.handleChange} />
</label>
<input type="submit" value="Submit">
</form>
);
}
};

ReactDOM.render(
< EssayForm/>,
document.getElementById('root')
);

选择标签

HTML中:

1
2
3
4
5
6
<select>
<option value="1">1</option>
<option value="2">2</option>
<option value="3" selected>3</option>
<option value="4">4</option>
</select>

React中:

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
class FlavorForm extends React.Component{

constructor(props){
super(props);
this.state = {value:'2'}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}

handleChange(event){
this.setState({value:event.target.value});
}

this.handleSubmit(event){
alert('选中’+this.target.value);
event.preventDefault();
}

render(){
return(
<form onSubmit = {this.handleSubmit}>
<select onChange = {this.handleChange} value={this.state.value}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
<input type="submit" value="Submit"/>
<form/>
);
}
}

ReactDOM.render(
<FlavorFrom />,
document.getElementById('root')
);

总的来说,<input type="text">,<textarea><select>所有的工作过程都比较相似,都可以接受value,可以通过它实现控制组件的属性

注意:

可以将数组传递给value属性,从而在select标签中选择多个选项

<select multiple = {true} value={[‘B’,’C’]}>

文件输入标签

它的值是只读的,所以它是React中不受控制的组件

HTML:

<input type = "file>"

处理多个输入

当需要处理多个受控input元素时,可以name为每个元素添加一个属性,并让成处理函数根据值来选择要执行的操作event.target.name

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
class Reservation extends React.Component{

constructor(props){
super(props);
this.state = {
isGoing:true,
numberOfGuset:2
};
this.handleInputChange = this.handleInputChange.bind(this);
}

handleInputChange(event){
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;

this.setState({
[name]:value
});
}

render(){
return{
<form>
<label>
Is going:
<input
name = "isGoing"
type = "checkbox"
checked = {this.state.isGoing}
onChange = {this.handleInputChange}
/>
</label>
<br/>
<label>
Number of guests
<input
name = "numberOfGuests"
type = "number"
value = {this.state.numberOfGuset}
onChange = {this.handleInputChange}
/>
</label>
</form>
}
}
}

ReactDOM.render(
<Reservation/>,
document.getElementById('root')
);

其中

1
2
3
this.setState({
[name]:value
});

相当于下面的这个es5代码:

1
2
3
var pratialState = {};
pratialState[name] = value;
this.setState(pratialState);

受控组件的替代品

使用受控组件有时会非常乏味,因为您需要为数据可以改变的每种方式编写一个事件处理程序,并通过React组件管理所有输入状态。当您将预先存在的代码库转换为React或将React应用程序与非React库集成时,这会变得特别烦人。在这些情况下,您可能需要检查不受控制的组件,这是实现输入表单的另一种技术。

九、提升状态Lifting State Up
通常几个组件需要放映相同的变化数据,建议将共享状态提升至最接受的共同祖先
温度计算器,用于计算在给定温度下水是否沸腾

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
funtion BoilingVerdict(props){
if(props.celsius >=100){
return <p>The water would boil</p>
}
return <p> The water would not bold.</p>
}

class Calulator extends React.Component{

constructor(props){
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {temperature:''};
}

handleChange(e){
this.setState({temperature:e.target.value});
}

render(){
const temperature = this.state.temperature;
return(
<fieldset>
<legend>Enter temperature in Celsius:</legend>
<input
value = {temperature}
onChange = {this.handleChange}
/>
<BoilingVerdict celsius={parseFloat(temperature)}/>
</fieldset>
)
}
}

ReactDOM.render(
<Calulator/>,
document.getElementById('root')
);

华氏 摄氏同时转换判断是否水沸腾(props/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
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
const scaleNames = {
c:'Celsius',
f:'Fahrenheit'
};

function toCelsius(fahrenheit){
return (fahrenheit-32)*5/9;
}

function toFahrenheit(celsius){
return (celsius*9/5)+32;
}

function tryConvert(temperature,convert){
const input = parseFloat(temperature);
if(Number.isNaN(input)){
return '';
}
const output = convert(input);
const rounded = Math.round(output * 1000)/1000;
return rounded.toString();
}

funtion BoilingVerdict(props){
if(props.celsius >=100){
return <p>The water would boil</p>
}
return <p> The water would not bold.</p>
}

class TemperatureInput extends React.Component{

constructor(props){
super(props);
this.handleChange = this.handleChange.bind(this);
};

handleChange(e){
this.props.onTemperatureChange(e.target.value);
}

render(){
const temperature = this.props.temperature;
const scale = this.props.scale;
return(
<fieldset>
<legend>Enter temperature in {scaleNames[scale]}:</legend>
<input value={temperature}
onChange={this.handleChange} />
</fieldset>
);
}
}

class Calculator extends React.Component{

constructor(props){
super(props);
this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
this.state = {temperature: '', scale: 'c'};
}

handleCelsiusChange(temperature){
this.setState({scale:'c',temprature});
}

handleFahrenheitChange(temperature){
this.setState({scale:'f',temprature});
}

render(){
const scale = this.state.scale;
const temperature = this.state.temperature;
const celsius = scale === 'f' ? tryConvert(temperature,toCelsius) : temperature;
const fahrenheit = scale === 'c' ? tryConvert(temperature,toFahrenheit) : temperature;
return(
<div>
<TemperatureInput
scale = "c"
temperature = {celsius}
onTemperatureChange = {this.handleCelsiusChange}
/>
<TemperatureInput
scale = "f"
temperature = {fahrenheit}
onTemperatureChange = {this.handleFahrenheitChange}
/>
<BoilingVerdict
celsius={parseFloat(celsius)} />
</div>
);
}
}

ReactDOM.render(
<Calculator />,
document.getElmentById('root')
);

参考链接:https://react.docschina.org/docs/hello-world.html

0%