纯粹函数的设计理念
什么是纯粹函数
纯粹函数的两大核心条件
条件1: 函数不修改被调用前就存在的变量与对象,只做纯粹的运算。
条件2: 保证函数相同的输入,就会有相同输出。
js
//定义一个外部变量
let num=0;
//纯粹函数
function add(x)
{//满足两大核心条件
//条件1:不修改函数外的任何变量
//条件2: 输入 x 固定,则输出必然是 x+1
return x+1;
}
//调用纯粹函数,传入外部的num
let a=add(num);
let b=add(num)
// num并未被修改,两次输入相同,得到的结果也相同
console.log(num,a) // 输出0 1
console.log(num,b) // 输出0 1
纯粹组件:React哲学
什么是纯粹的组件
纯粹组件的两大核心条件
react模仿纯粹函数的设计理念,提出纯粹组件的设计理念同样需要满足两个核心条件:
条件1:不修改组件渲染前存在的对象与变量。
条件2:相同的输出,返回相同的jsx
反例:什么样的组件不纯粹
条件1:组件内部对外部依赖值进行更改
条件2:多次渲染组件时输入相同,但是返回到jsx却不同
如果满足上诉两个任意一个条件,那么这个组件将不纯粹
jsx
import React from 'react'
//定义一个外部变量
let num=0
function Child(){
// 组件修改外部依赖值
// 导致相同的输入下,每次返回的
num++;
return <div>{num}</div>
}
export default function App() {
return (
<div>
<Child></Child>
<Child></Child>
<Child></Child>
</div>
)
}

为什么需要保持组件纯粹
- 保证组件渲染的独立性
所谓独立性:
如果组件渲染时修改了外部预先存在的变量会导致:
-
后续依赖这些变量与对象的组件渲染产生不确定性,这就导致依赖同一变量的组件需要保证渲染的顺序。
-
但是组件在浏览器中的渲染时机是不确定的。如果组件渲染结果,依赖组件间渲染的顺序。就会导致既定逻辑失效,从而产生错误。
所以我们要排除这种不确定性,使得react组件使用更加安全,让组件渲染之间的渲染逻辑更加独立
- 保证组件渲染的确定性
所谓确定性
确定性建立在独立性之上
- 当组件渲染时独立地不影响外部组件的渲染逻辑,由此保证组件外部组件渲染的确定性,能够预先避免许多错误
- 没有保持纯粹性的反面案例
jsx
// 全局变量(外部状态)
let globalCount = 0;
// 组件A:渲染时修改外部变量
function ComponentA() {
globalCount += 1; // 修改外部变量
return <div>组件A:{globalCount}</div>;
}
// 组件B:依赖同一个外部变量
function ComponentB() {
return <div>组件B:{globalCount * 2}</div>;
}
// 父组件:同时渲染A和B
function Parent() {
return (
<div>
<ComponentA />
<ComponentB />
</div>
);
}
- 理想顺序(A先渲染,B后渲染):globalCount 先变成 1,B渲染2 → 结果:A=1,B=2;
- 实际可能的顺序(React调度导致B先渲染):B先读取 globalCount=0渲染0,再A把 globalCount 改成 1→结果:A=1,B=0;
保持纯粹 != 不可修改
局部突变
- 什么是局部突变
在react官方文档中的定义其实是"局部mutation",即在渲染时修改组件内部"刚刚创建对象或者变量"。
- 这样的局部突变不会影响外部组件的渲染逻辑,只影响组件内部的逻辑。
- 利用局部突变保持组件的纯粹性
jsx
// 全局变量(外部状态)
let globalCount = 0;
// 组件A:渲染时不修改外部变量
function ComponentA() {
let Count=globalCount+1
return <div>组件A:{Count}</div>;
}
// 组件B:依赖同一个外部变量,但是不直接修改外部变量
function ComponentB() {
let Count=(globalCount+1)*2
return <div>组件B:{Count * 2}</div>;
}
// 父组件:同时渲染A和B
function Parent() {
return (
<div>
<ComponentA />
<ComponentB />
</div>
);
}
- 无论A,B谁先渲染,都改变不了渲染结果:A=1,B=2
- 最终使组件重新保持"纯粹"
用"更新" 替换 "修改"
使用"状态"管理变量
- 为了保证组件的纯粹性,并不意味着组件渲染依赖的外部变量完全就不能改变了。可以通过"状态"来管理外部变量。
"状态"管理变量的好处
- 当变量需要被修改时,不再直接对其进行变更。而是等待组件渲染完成后,更新并将修改值传入组件中。由此保持组件渲染时的独立性与确定性
jsx
import { useState } from 'react';
// 显示购物车数量的组件(纯粹组件:只依赖 props,无任何副作用)
function PureCartCount({ count }) {
// 只读取 props,不修改任何外部数据,相同 props 必然渲染相同结果
return <div>购物车数量:{count}</div>;
}
// 控制数量增减的组件(不直接修改数据,只发起更新请求)
function PureCountControls({ onAdd, onSubtract }) {
// 不触碰任何变量,只通过父组件传入的函数发起"更新请求"
return (
<div>
<button onClick={onSubtract}>-</button>
<button onClick={onAdd}>+</button>
</div>
);
}
// 父组件:用状态管理变量,统一控制更新逻辑
function App() {
// 用 useState 管理购物车数量(状态由 React 管控,初始值 1)
const [cartCount, setCartCount] = useState(1);
// 数量"更新"逻辑:生成新值,不修改原状态
const handleAdd = () => {
// 关键:不直接改 cartCount(如 cartCount +=1),而是通过 setCartCount 生成新值
setCartCount(prevCount => prevCount + 1);
};
const handleSubtract = () => {
// 生成新值(原 cartCount 保持不变,直到 React 触发重新渲染)
setCartCount(prevCount => Math.max(1, prevCount - 1)); // 避免数量小于 1
};
return (
<div>
{/* 新状态通过 props 传入纯粹组件,组件只"读"不"写" */}
<PureCartCount count={cartCount} />
{/* 传递更新函数,让子组件发起更新请求 */}
<PureCountControls onAdd={handleAdd} onSubtract={handleSubtract} />
</div>
);
}
export default App;