blog: 添加React笔记

This commit is contained in:
xugaoyi 2021-03-26 18:22:32 +08:00
parent 108e7973bf
commit 87444e54f8
3 changed files with 372 additions and 5 deletions

View File

@ -1,6 +1,6 @@
---
title: 状态提升
date: 2021-03-24 17:56:40
title: 状态提升 (共享状态)
date: 2021-03-26 09:56:40
permalink: /pages/f0e3d2/
categories:
- 《React》笔记
@ -9,4 +9,172 @@ tags:
- React
---
# 09. 状态提升
# 09. 状态提升 (共享状态)
通常,**多个组件需要反映相同的变化数据,这时我们建议将共享状态提升到最近的共同父组件中去**。
在 React 中,**将多个组件中需要共享的 state 向上移动到它们的最近共同父组件中,便可实现共享 state。这就是所谓的“状态提升”**
两个输入框共享数据的例子:
```jsx
const scaleNames = {
c: '摄氏度',
f: '华氏度'
};
// 转摄氏度
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);
// Math.round返回一个数字四舍五入后的整数
const rounded = Math.round(output * 1000) / 1000;
return rounded.toString();
}
// 水是否会沸腾
function BoilingVerdict(props) {
if (props.celsius >= 100) {
return <p>水会沸腾.</p>;
}
return <p>水不会沸腾.</p>;
}
// 子组件 - 输入框
class TemperatureInput extends React.Component {
constructor(props) {
super(props); // 接收父组件传入props
this.handleChange = this.handleChange.bind(this); // 绑定回调函数并修正this
}
// 处理change
handleChange(e) {
// e是合成事件对象通过e.target.value 取值
// 调用父组件传入的onTemperatureChange函数并传值
this.props.onTemperatureChange(e.target.value);
// 当子组件输入框值改变时调用父组件的onTemperatureChange方法并传出值。
// 另外onTemperatureChange命名方式`在<子组件>变更`
}
render() {
// 接收父组件传入的温度值
const temperature = this.props.temperature;
// 接收父组件传入的衡量方式
const scale = this.props.scale;
return (
<fieldset>
<legend>输入温度-{scaleNames[scale]}:</legend>
<input value={temperature}
onChange={this.handleChange} />
</fieldset>
);
}
}
// 父组件 - 计算器
class Calculator extends React.Component {
constructor(props) {
super(props); // 接收父组件传入props
// 绑定事件回调并修正this
this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
// 创建初始状态值
this.state = {temperature: '', scale: 'c'};
}
// 处理`摄氏度`变更
handleCelsiusChange(temperature) {
// temperature接收到子组件传来的参数并通过setState修改状态
this.setState({scale: 'c', temperature});
}
// 处理`华氏度`变更
handleFahrenheitChange(temperature) {
// temperature接收到子组件传来的参数并通过setState修改状态
this.setState({scale: 'f', temperature});
}
// 渲染函数每当state改变都会调用
render() {
// 取得当前state下的值
const scale = this.state.scale;
const temperature = this.state.temperature;
// 根据scale值取得相应的温度数据
const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
// 返回渲染的元素
// 插入子组件TemperatureInput传入相应的参数onTemperatureChange指定为当前组件的回调函数
return (
<div>
<TemperatureInput
scale="c"
temperature={celsius}
onTemperatureChange={this.handleCelsiusChange} />
<TemperatureInput
scale="f"
temperature={fahrenheit}
onTemperatureChange={this.handleFahrenheitChange} />
<BoilingVerdict
celsius={parseFloat(celsius)} />
</div>
);
}
}
// 渲染DOM
ReactDOM.render(
<Calculator />,
document.getElementById('root')
);
```
[**在 CodePen 上尝试**](https://codepen.io/gaearon/pen/WZpxpz?editors=0010)
::: note
1. 父组件给所有子组件传入state的值
2. 子组件修改值时调用父组件的方法并把值传出
3. 父组件接收到值之后修改state
4. state被修改之后重新执行render函数并回到第1步
:::
### 小结
- 任何可变数据应当只有一个相对应的唯一“数据源”
- 通常state 都是首先添加到需要渲染数据的组件中去
- 然后,如果其他组件也需要这个 state那么你可以将它提升至这些组件的最近共同父组件中
- 你应当依靠[自上而下的数据流](https://zh-hans.reactjs.org/docs/state-and-lifecycle.html#the-data-flows-down),而不是尝试在不同组件间同步 state。
- “存在”于组件中的任何 state仅有组件自己能够修改它
- 如果某些数据可以由 props 或 state 推导得出,那么它就不应该存在于 state 中。如上例中经过tryConvert方法转换的后的值。
### React开发者工具debug
当你在 UI 中发现错误时,可以使用 [React 开发者工具](https://github.com/facebook/react/tree/master/packages/react-devtools) 来检查问题组件的 props并且按照组件树结构逐级向上搜寻直到定位到负责更新 state 的那个组件。

View File

@ -1,6 +1,6 @@
---
title: 组合vs继承
date: 2021-03-24 17:56:40
date: 2021-03-26 12:56:40
permalink: /pages/9ae8e8/
categories:
- 《React》笔记
@ -10,3 +10,190 @@ tags:
---
# 10.组合vs继承
React 有十分强大的组合模式。我们**推荐使用组合而非继承来实现组件间的代码重用**。
## 包含关系 (组件组合)
有些组件无法提前知晓它们子组件的具体内容。在 `Sidebar`(侧边栏)和 `Dialog`对话框等展现通用容器box的组件中特别容易遇到这种情况。
### props的children属性类似vue的插槽
我们建议这些组件使用一个**特殊的 `children` prop 来将他们的子组件传递到渲染结果中**
```jsx
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
// children 是特殊的prop在父组件中没有显式声明
{props.children}
</div>
);
}
function WelcomeDialog() {
return (
<FancyBorder color="blue">
// 子组件标签之间的内容被当做 props.children 传入
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
```
:::note
类似于vue中的插槽
:::
### props传入组件类似vue命名插槽
少数情况下,你可能需要在一个组件中预留出几个“洞”。这种情况下,我们可以不使用 `children`,而是自行约定:将所需内容传入 props并使用相应的 prop。
```jsx
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">
{props.left}
</div>
<div className="SplitPane-right">
{props.right}
</div>
</div>
);
}
function App() {
// props传入组件
return (
<SplitPane
left={
<Contacts />
}
right={
<Chat />
} />
);
}
```
`<Contacts />``<Chat />` 之类的 React 元素本质就是对象object所以你可以把它们当作 props像其他数据一样传递。**你可以将任何格式的数据作为 props 进行传递。**
:::note
类似于vue中的命名插槽
:::
## 特例关系(字符串与组件组合)
有些时候,我们会把一些组件看作是其他组件的特殊实例,比如 `WelcomeDialog` 可以说是 `Dialog` 的特殊实例。
在 React 中,我们也可以通过组合来实现这一点。“特殊”组件可以通过 props 定制并渲染“一般”组件:
```jsx
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
// 这里props.title非组件对象而是字符串
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
</FancyBorder>
);
}
function WelcomeDialog() {
return (
<Dialog
title="Welcome"
message="Thank you for visiting our spacecraft!"
/>
);
}
```
## class方式的组件组合
组合也同样适用于以 class 形式定义的组件。
```jsx
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
{props.children}
</FancyBorder>
);
}
class SignUpDialog extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSignUp = this.handleSignUp.bind(this);
this.state = {login: ''};
}
render() {
return (
<Dialog title="Mars Exploration Program"
message="How should we refer to you?">
<input value={this.state.login}
onChange={this.handleChange} />
<button onClick={this.handleSignUp}>
Sign Me Up!
</button>
</Dialog>
);
}
handleChange(e) {
this.setState({login: e.target.value});
}
handleSignUp() {
alert(`Welcome aboard, ${this.state.login}!`);
}
}
```
## 那么继承呢?(不推荐)
在 Facebook我们在成百上千个组件中使用 React。我们并没有发现需要使用继承来构建组件层次的情况。
Props 和组合为你提供了清晰而安全地定制组件外观和行为的灵活方式。**注意:组件可以接受任意 props包括基本数据类型React 元素以及函数**。
如果你想要在组件间复用非 UI 的功能,我们建议将其提取为一个单独的 JavaScript 模块如函数、对象或者类。组件可以直接引入import而**无需通过 extend 继承它们**。

View File

@ -1,6 +1,6 @@
---
title: React哲学
date: 2021-03-24 17:56:40
date: 2021-03-26 17:56:40
permalink: /pages/c689bf/
categories:
- 《React》笔记
@ -10,3 +10,15 @@ tags:
---
# 11.React哲学
React 最棒的部分之一是引导我们思考如何构建一个应用。在这篇文档中,我们将会通过 React 构建一个可搜索的产品数据表格来更深刻地领会 React 哲学。
## 从设计稿开始
![Mockup](https://zh-hans.reactjs.org/static/1071fbcc9eed01fddc115b41e193ec11/d4770/thinking-in-react-mock.png)
### 第一步:将设计好的 UI 划分为组件层级
**首先,你需要在设计稿上用方框圈出每一个组件(包括它们的子组件)**,并且以合适的名称命名。如果你是和设计师一起完成此任务,那么他们可能已经做过类似的工作,所以请和他们进行交流!他们的 Photoshop 的图层名称可能最终就是你编写的 React 组件的名称!