初识React
最近在逛掘金的时候,无意浏览到挺多关于React的文章。由于平时也只是接触Vue比较多,React也只是道听途说的路上,借此机会去学习一下React,直接上手React官方教程就行,这篇文章是跟着教程走的时候的总结。
开发环境搭建
首先官网给的是两种方式去搭建环境:1、直接在浏览器中codePen编写代码;2、直接搭建本地开发环境。我这里选择的是第二种
npx create-react-app my-app过程有点漫长,耐心等待一下就好。当你看到
We suggest that you begin by typing:
cd my-app
yarn start
Happy hacking!
就证明安装成功。不过我们不急着跟着提示弄,我们跟着官方文档就行。当然你也可以先试试项目运行是什么效果的,看个人选择。跟着官方教程的话,我们需要删掉src文件夹下的所有文件,但不包括src本身,然后跟着教程搬一下代码就好,这里就不做过多解释了。
分析初始代码
组件传值
顺利按照官方文档走的,可以看到index.js里面只有3个组件Square,Board,Game,他们是组成棋盘的主体。而Square组件在Board组件里调用renderSquare了9次,
class Board extends React.Component {
renderSquare(i) {
return <Square />;
}
render() {
const status = 'Next player: X';
return (
<div>
<div className="status">{status}</div>
<div className="board-row">
{this.renderSquare(0)}
{this.renderSquare(1)}
{this.renderSquare(2)}
</div>
<div className="board-row">
{this.renderSquare(3)}
{this.renderSquare(4)}
{this.renderSquare(5)}
</div>
<div className="board-row">
{this.renderSquare(6)}
{this.renderSquare(7)}
{this.renderSquare(8)}
</div>
</div>
);
}
}
分别对应9个Button按钮,通过代码,我们可以初步猜测renderSquare就是用来渲染按钮的,但是肯定有后续操作才会特意去创建一个渲染方法去render按钮。作为证明,我们也可以直接更改{this.renderSquare(n)}为<Square />也可以正常显示的。
由此可以猜想,renderSquare(n)中的n无非就是用作传递位置参数,对应棋盘的方格位置。
但是要怎么传到Square组件内部,就是需要学习的。在下一步就能看到,直接在renderSquare方法里直接return <Square value={i} />;,这样就是传递参数,然后在Square组件里通过{this.props.value}就可以访问到参数。
猜测一下就是组件参数访问可以通过{this.props.key}访问,传值的话直接通过<组件名 key={value} />形式传递。这个形式和Vue父子组件间的数据通信有点大同小异。
//证明
class Square extends React.Component {
render() {
return (
<button className="square" data-dex={this.props.buttonIndex}>
{this.props.buttonIndex}
</button>
);
}
}
class Board extends React.Component {
renderSquare(i) {
return <Square value={i} buttonIndex={i} />;
}
//··············省略
}
组件交互
在交互方面,React在监听事件方面更加相似于原生的DOM元素,例如教程中的click事件,直接在组件上写onClick,使用驼峰式命名,并且后面直接写对应的方法即可,例如onBlur,onMouseEnter等等。
教程里直接是在组件上使用ES6的箭头函数,理由是以防this的指向错误,经过测试,如果直接function () {console.log(this)},这里的this指向的是undefinded,如果改成箭头函数,this则会指向组件本身。
当然,如果真正要绑定事件,肯定不会直接把整个Funciton写到render上面,肯定是绑定通过绑定函数名去绑定事件。这里还是参考官方文档--事件处理。
后面教程上给出了一个state的概念,初看以为类似于vVuex中的state,但是仔细一想应该不是,因为这个state更贴切来说是对应自己组件的,更类似与Vue里面的data属性。
而他改变state的方式,和小程序基本一样,也是通过this.setSate方式,然后就回去重新渲染React组件。另外,state需要定义在组件内部的构造函数constructor内部,且固定super(props)
class componentName extends React.Component {
constructor(props) {
super(props);
this.state = {
stateName: 'Hello World',
};
}
render() {
return (
<h1>
{this.state.stateName}
</h1>
);
}
}
以上都是官方教程内的最基本教程,分别简单地介绍了:“父子组件传值”,“组件交互方式”。但是也可以看到React是如何进行变量赋值,及组件渲染,组件嵌套方式。
后面的教程会更加对基础知识深入一点,对组件的设计以及数据方面会更加合理。
完善组件
当你遇到需要同时获取多个子组件数据,或者两个组件之间需要相互通讯的情况时,需要把子组件的 state 数据提升至其共同的父组件当中保存。之后父组件可以通过 props 将状态数据传递到子组件当中。这样应用当中所有组件的状态数据就能够更方便地同步共享了。
由于上面的教程上的Square组件维护了自身的一个state,但是如果Board父组件需要收集组件的state的时候则会造成困难,虽然教程里的只有9个子组件,但是如果日后遇到维护多个子组件的state时候,就会难进行维护。
所以根据官网这段话,可以把子组件的state提升到父组件上,然后通过前文所说的父子组件进行props分发,这样更容易维护。用最简单的解释方法就是,把“儿子”的数据收集起来,统一交由“父亲”管理,然后通过“父亲”逐一分发给每个“儿子”。所以通过上面教程那样,将收集回来的数据,通过参数化形式传递到子组件。
那么这个时候,原本存在于Square组件内部的state不再拥有,因为已经“上交”给父组件(Board),所以可以删掉对应的state,并且对应的onClick事件也要更改,因为原方法是直接更改this.setState。
对于初学React的我来说这个时候懵了,因为不知道如何通过点击事件去更改父组件(Board),当然在Vue里面就是一个简单的父子组件通讯而已。但是在React中,教程是通过“事件作为参数传递”。也就是把处理父组件Board的state的方法作为参数传递到子组件Square,然后子组件直接绑定一个onCLick事件去
间接触发父组件Board的setState。这种上交管理数据,并且方法互相触发,有循环回路(Board-Square(受控组件)),被收集数据的组件,统称为受控组件。
class Square extends React.Component {
render() {
return (
<button className="square"
onClick={()=>{this.props.onClick()}}> //重点1
{this.props.value}
</button>
);
}
}
class Board extends React.Component {
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)}} //重点
/>;
}
render() {
//··············省略
}
}
函数组件
函数组件,在我从字面上理解就是把组件当成函数去编写,不再通过继承React.Component去定义类,而是通过直接一个function声明组件,直接return所需要的元素,但是这么做的前提是不包含state。
井字棋获胜方法
教程里还提供了一个function专门用来计算什么情况是赢家。顺路啰嗦分析一下这个函数是怎么计算的。
function calculateWinner(squares) {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
]; //改二维数组存放的就是井字棋个种获胜情况。行排3种,竖排3种,对角线2种。
for (let i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i];
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { //只要不为NULL,并且3个位置对应相等,即返回对应符号
return squares[a];
}
}
return null;
}
后续
在教程最后的地方,也就是根据用户操作储存历史操作记录。然后根据数组进行循环生成li。在做这一步的时候,和官网显示的一样,console报错。其实根据含义就能猜测到基本和v-for内部的key一个道理,循环生成的元素最好添加唯一key。
每当一个列表重新渲染时,React 会根据每一项列表元素的 key 来检索上一次渲染时与每个 key 所匹配的列表项。如果 React 发现当前的列表有一个之前不存在的 key,那么就会创建出一个新的组件。如果 React 发现和之前对比少了一个 key,那么就会销毁之前对应的组件。如果一个组件的 key 发生了变化,这个组件会被销毁,然后使用新的 state 重新创建一份。
总结
教程很简短也易懂,基础概念解释得挺容易懂,我个人是比较喜欢直接上手一些小demo去接触新技术。这样更容易理解每个技术得用法,再慢慢通过用法去加深理解。这篇文章算是刚入门时候的心得以及理解,可能不适合其他人观看,但是可以借鉴一下,看看我和其他人的想法有什么不一样。