react
在企业项目中已经变成了一个必不可少的UI
框架,从过去早期有jquery
,后面有bootstrap
兴起,jquery
可以说二次封装的原生js
,bootstarp
可以快速搭建一个精美网页,现在基本很少用bootstrap
和jquery
了,基本上vuejs
,react
,angular
三分天下,国内vuejs
和react
居多,angular
很少用,本文是一篇笔者关于react
相关的笔记,希望看完在项目中有所思考和帮助。
在开始本文之前,主要会从以下几个方面去认识学习react
1、没有概念,用实际例子感受react
核心思想
2、react
数据流是怎么样,父子通信,react
是如何更新数据
3、class
组件与纯函数
组件
4、react
的状态提升
5、react
组合概念
6、react
设计哲学
正文开始...
class组件
react
的理念就是构建UI的一个库,很大一个特征就是申明式
,组件化
,跨平台
- 申明式
import React from "react";
import ReactDOM from 'react-dom/client';
class HelloMessage extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
render() {
return (
<div>
Hello {this.props.name}
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('hello-example'))
root.render(<HelloMessage name="Web技术学苑" />);
从以上我们知道就是用class
申明了一个HelloMessage
的组件,继承了React.Component
组件,然后通过root.render(<HelloMessage />)
挂载到dom
上,我们也会发现HelloMessage
这个class
组件中的render
中返回的就是一个jsx
,这就是挂载在dom
上的具体内容
纯函数组件
以上是class
方式写的一个组件,但是从react16.8
版本后就出现了hook
,也就是过去纯函数组件没有自己的状态,但是有了hook
后,纯函数组件就可以有自己的状态了。我们完全可以用函数组件
和hook
替代class
组件,上面一段代码,如果用函数组件
就是下面这样的
import React, { useState } from "react";
import ReactDOM from 'react-dom/client';
const HelloMessage = (props) => {
const [count, setCount] = useState(0);
return (
<div>
Hello {props.name}
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('hello-example'))
root.render(<HelloMessage name="Web技术学苑" />);
状态提升
我们知道每个组件有自己的state
,如果同一个组件内部都是自己的state
,那么组件之间就是互相独立,但是此时我想让一个组件输入值,也会影响另一个组件,那么此时就需要两个组件的数据依赖来源就必须提升到父组件里去,所以这就是状态提升。
我们以人民币换算美元汇率的例子加深对状态提升
概念的理解
import React from "react";
class List extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
price: 0,
type: 0, // 0 人民币 1 代表美元
}
}
handleAdd = () => {
// this.setState({
// count: ++this.state.count
// })
this.setState(state => {
return {
...state,
count: state.count + 1
}
})
}
handleReduce = () => {
this.setState(state => {
return {
count: state.count - 1
}
})
}
handleRmbInput = (e) => {
const val = e.target.value;
console.log(e)
this.setState({
price: val,
type: 0
})
}
handleDollInput = (e) => {
const val = e.target.value;
this.setState({
price: val,
type: 1
})
}
render() {
const { count, price, type } = this.state;
const RmbInput = ({ price, handleChange }) => {
return (<fieldset>
<legend>人民币</legend>
<input value={price}
onChange={handleChange} />
</fieldset>)
};
const DollarInput = ({ price, handleChange }) => {
return (<fieldset>
<legend>美元</legend>
<input value={price}
onChange={handleChange} />
</fieldset>)
};
const rmbVal = type === 0 ? price : price * 7.34;
const dollVal = type === 1 ? price : 0.14 * price;
return (<div className="list-app" style={{ padding: '10px' }}>
<hr />
<button onClick={this.handleAdd}>+</button>
<span>{count}</span>
<button onClick={this.handleReduce}>-</button>
<hr />
<RmbInput handleChange={this.handleRmbInput} price={rmbVal} />
<hr></hr>
<DollarInput handleChange={this.handleDollInput} price={dollVal} />
</div>)
}
}
export default List
我们看到List
这个组件返回的有RmbInput
与DollarInput
组件,你会发现实际上这两个组件的共同特征就是都传入了两个props
到子组件中,注意其中一个是传入的是方法handleChange
,通常在react
中数据流是单向的,所以修改传入的子组件的props
,通常是通过父组件的传入子组件的回调方法去修改传入子组件的props
比如说下面这样一段伪代码
// Parant
function Parent() {
const { name, setName} = useState("Web技术学苑");
return (<div>
<h1>{name}</h1>
<Child name={name} onChangeName={val => setName(val)}/>
</div>)
}
// Child
function Child(props) {
return (<div>
<input value={props.name} onChange={e => props.onChangeName(e.target.value)}/>
</div>)
}
最后我们看下最终的效果
因此一个计算汇率的功能就OK了
但是我们发现实际上RmbInput
与DollarInput
做的事情都是非常类似的,只有名称不一样,因此我们可以将这两个组件合并成一个组件,只需传入一个参数来判断名称即可。
import React from "react";
class List extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
price: 0,
type: 0, // 0 人民币 1 代表美元
}
}
...
render() {
const { count, price, type } = this.state;
// 合并成一个组件了
const ExchangeInput = ({ price, handleChange, type }) => {
return (<fieldset>
<legend>{type === "RmbInput" ? '人民币' : '美元'}</legend>
<input value={price}
onChange={handleChange} />
</fieldset>)
}
const rmbVal = type === 0 ? price : price * 7.34;
const dollVal = type === 1 ? price : 0.14 * price;
return (<div className="list-app" style={{ padding: '10px' }}>
<hr />
<button onClick={this.handleAdd}>+</button>
<span>{count}</span>
<button onClick={this.handleReduce}>-</button>
<hr />
<ExchangeInput handleChange={this.handleRmbInput} price={rmbVal} type="RmbInput"></ExchangeInput>
<hr/>
<ExchangeInput handleChange={this.handleDollInput} price={dollVal} type="DollarInput"></ExchangeInput>
</div>)
}
}
export default List
因此从状态提升
来看,react
组件的state
本该相互独立,但是如果想实现一个组件修改关联另一个组件修改,那么数据来源必须依赖父组件,所以也就只能把当前组件的state
提升到父组件里去,从而实现了状态提升。
这里我们也发现,react
提供给子组件的通信就是props
,修改当前组件的state
就是依赖setState
,要想修改父组件数据就是通过props
传入子组件的回调去修改的。
react组合
在react组合
类似vue
的插槽一样的概念,不过有些区别
props.children
就是默认渲染所有父组件的插槽
内容
import React from "react";
const Title = (props) => {
return (<div className="title" style={{ display: 'flex' }}>
{props.children}
</div>)
}
const About = () => <div>
<Title>
<h1>Web技术学苑</h1>
<div>Maic</div>
</Title>
</div>
export default About;
这样你看到的结果就是下面这样的
你也可以通过props
做类似的vue
具名插槽的功能
import React from "react";
const Title = (props) => {
return (<div className="title" style={{ display: 'flex' }}>
{props.children}
</div>)
}
const Content = (props) => {
return (<div className="content" style={{ ...props.style }}>
{props.left}
{props.children}
{props.right}
</div>)
}
const About = () => {
const Left = () => (<div>left</div>);
const Right = () => (<div>right</div>);
return (<div>
<Title>
<h1>Web技术学苑</h1>
<div>Maic</div>
</Title>
<Content
style={{ padding: '10px' }}
left={<Left />}
right={<Right />}
>
<div>center</div>
</Content>
</div>)
}
export default About;
我们发现Content
这个组件的props
可以是对象,可以是jsx
,也可以是函数,当子组件用父组件的props
时,我们就当它变量一样在子组件中使用,因此react
中的props
是相当灵活的。
react哲学
react哲学
官方已经用了一篇文章来阐述,通篇下来,官网已经用了一个实际例子来解释react
的哲学思想,总结下来,其实就是以下几点。
1、如何将复杂的UI模块拆分成更细粒度的组件,我们将一个页面拆分成组件,组件依赖数据更清晰,组件之间的耦合度更低。
2、组件的接口props
与state
,要明确知道当前组件的state
是应该放在顶层父组件中,还是当前自身组件
3、因为react
数据流是单向的,在实现父子组件数据流双向过程中,通常用回调
来传递子组件向父组件传递的数据
官方把react哲学
分成了五步走概念,关于组件的拆分,其实我们心里在写代码之前就应该明白哪些组件适合独立拆分,哪些组件不用拆分,当一个页面过于复杂时,此时我们考虑拆分组件的同时,也需要考虑是否组件过于拆分带来的负担,这个因具体情况而定
总结
理解
react
构建UI的两种方式,一种是class
方式,一种纯函数
组件方式react
数据通信,父子组件如何通信当多个组件存在互相影响时,此时得考虑
状态提升
,每个独立的组件状态数据依赖来源必须从顶层组件中传入,并且当需要更新props
时,考虑回调函数修改在
react
中实现vue
插槽的功能,也就是react
的组合,props.children
会默认渲染父组件插槽
功能,通过props
指定jsx
可以可以实现具名插槽功能理解
react哲学
思想,让我们更灵活的运用react
构建一个完整,维护性很强的web
应用