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应用




