react学习笔记(一)

通过官网的九宫格棋子的小例子学习一下react的基础使用,不知道与vue的差别大不大。

组件

react中自定义组件通过继承 React.Component 来实现,通过render方法渲染所希望看到的dom结构,实际上render返回的是一个react元素,使用JSX语法能让我们直接书写dom结构而不需重复使用React.createElement来创建,因为使用了Babel来转换。

1
2
3
4
5
6
7
8
9
return (
<div className="shopping-list">
<h1>Shopping List for {this.props.name}</h1>
</div>
)
编译后
return React.createElement('div', {className: 'shopping-list'},
React.createElement('h1', /* ... h1 children ... */)
);

组件通信

官网的例子中有三个组件:Square、Board、Game,其中Square 组件渲染了一个单独的 <button>。Board 组件渲染了 9 个方块。Game 组件渲染了含有默认值的一个棋盘。

通过props实现传值(vue中也可以通过props实现父子组件的传值,子组件通过props接收父组件的值)

1
2
3
4
5
6
7
8
9
10
11
12
class Board extends React.Component {
renderSquare(i) {
return <Square value={i} />; }
}
class Square extends React.Component {
render() {
return (
<button className="square">
{this.props.value} </button>
);
}
}

父组件Board通过value这个prop将i传递给Square,Square通过this.props.value获取i的值

组件交互

实现点击棋格落下’X’棋子,在Square中利用state保存点击过的地方,button绑定onclick点击事件,每次点击修改state中的值。与vue不同的是,react中直接包含了state属性,而vue中的state是存在与vuex中的。发现react中方法之间不需要写逗号👍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Square extends React.Component {
constructor(props) {
super(props);
this.state = {
value: null,
};
}

render() {
return (
<button
className="square"
onClick={() => this.setState({value: 'X'})} >
{this.state.value} </button>
);
}
}

需要注意的是,在JS中定义子类(Square)的构造函数时,需要调用super,在含有构造函数的react组件中,构造函数要以super(props)开头。

onclick事件中注意添加() =>,实现触发事件时才执行里面的操作,不添加的话将每次渲染都触发操作。

状态提升

读了官网的描述后,

1
当你遇到需要同时获取多个子组件数据,或者两个组件之间需要相互通讯的情况时,需要把子组件的 state 数据提升至其共同的父组件当中保存。之后父组件可以通过 props 将状态数据传递到子组件当中。这样应用当中所有组件的状态数据就能够更方便地同步共享了。

感觉状态提升应该是指提升子组件的状态,然后将子组件的状态state直接保存在父组件中实现状态同步。

具体就是让子组件Square更新父组件Board的state,由于每个子组件中的state都是私有的,不能直接通过 Square 来更新 Board 的 state,需要从父组件Board向子组件传递一个函数,当子组件被点击时,触发handleClick函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Board:
constructor(props) {
super(props);
this.state = {
squares: Array(9).fill(null),
};
}
handleClick(i) { const squares = this.state.squares.slice(); squares[i] = 'X'; this.setState({squares: squares}); }

renderSquare(i) {
return (
<Square
value={this.state.squares[i]}
onClick={() => this.handleClick(i)} />
);
}
Square:
return (
<button
className="square"
onClick={() => this.props.onClick()} >
{this.props.value} </button>

可以看到函数和值的传递都是类似的,通过子组件通过this.props调用值或者是函数。

此时,Board 组件完全控制了 Square 组件,将 Square 组件称做“受控组件”

不可变性

注意,此时handleClick使用.slice()返回一个新的数据副本,而不是直接修改数据,这样可以跟踪数据变化,也便于撤销回复操作的开发。

1
不可变性最主要的优势在于它可以帮助我们在 React 中创建 pure components。我们可以很轻松的确定不可变数据是否发生了改变,从而确定何时对组件进行重新渲染。

官网这段话,后半段很好理解,前半段不懂,先占坑。

函数组件

将Square 类替换成函数,函数直接接收参数,组件直接调用函数

1
2
3
4
5
6
7
function Square(props) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}

函数中直接写props而不是this.props,因为已经不是一个对象了,同时onclick去掉了()=>,因为触发函数就直接执行onclick。

保存历史

使用history数组保存历史,将其放在顶层的Game组件中使用,也是一种状态提升,所以去掉子组件 Board的state,通过props来传递值。官网还用了一个map方法来映射代表按钮的 React 元素,然后可以展示出一个按钮的列表,点击这些按钮,可以“跳转”到对应的历史步骤。

1
<li key={user.id}>{user.name}: {user.taskCount} tasks left</li>

使用key来标识列表项,实现可以分步展示history。

查看评论