blog: 添加React笔记
This commit is contained in:
parent
108e7973bf
commit
87444e54f8
|
|
@ -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 的那个组件。
|
||||
|
||||
|
|
|
|||
|
|
@ -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 继承它们**。
|
||||
|
||||
|
|
|
|||
|
|
@ -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 哲学。
|
||||
|
||||
|
||||
|
||||
## 从设计稿开始
|
||||
|
||||

|
||||
|
||||
### 第一步:将设计好的 UI 划分为组件层级
|
||||
|
||||
**首先,你需要在设计稿上用方框圈出每一个组件(包括它们的子组件)**,并且以合适的名称命名。如果你是和设计师一起完成此任务,那么他们可能已经做过类似的工作,所以请和他们进行交流!他们的 Photoshop 的图层名称可能最终就是你编写的 React 组件的名称!
|
||||
|
|
|
|||
Loading…
Reference in New Issue