1 Why Hook?
1.1 从React组件设计理论说起
React以一种全新的编程范式定义了前端开发约束,它为视图开发带来了一种全新的心智模型:
React认为,UI视图是数据的一种视觉映射,即UI = F(DATA),这里的F需要负责对输入数据进行加工、并对数据的变更做出响应
公式里的F在React里抽象成组件,React是以组件(Component-Based)为粒度编排应用的,组件是代码复用的最小单元
在设计上,React采用props属性来接收外部的数据,使用state属性来管理组件自身产生的数据(状态),而为了实现(运行时)对数据变更做出响应需要,React采用基于类(Class)的组件设计!
除此之外,React认为组件是有生命周期的,因此开创性地将生命周期的概念引入到了组件设计,从组件的create到destory提供了一系列的API供开发者使用
这就是React组件设计的理论基础,我们最熟悉的React组件一般长这样:
// React基于Class设计组件
class MyConponent extends React.Component {
// 组件自身产生的数据
state = {
counts: 0
}
// 响应数据变更
clickHandle = () => {
this.setState({ counts: this.state.counts++ });
if (this.props.onClick) this.props.onClick();
}
// lifecycle API
componentWillUnmount() {
console.log(‘Will mouned!’);
}
// lifecycle API
componentDidMount() {
console.log(‘Did mouned!’);
}
// 接收外来数据(或加工处理),并编排数据在视觉上的呈现
render(props) {
return (
<>
Add
</>
);
}
}
1.2 Class Component的问题
1.2.1 组件复用困局
组件并不是单纯的信息孤岛,组件之间是可能会产生联系的,一方面是数据的共享,另一个是功能的复用:
对于组件之间的数据共享问题,React官方采用单向数据流(Flux)来解决
对于(有状态)组件的复用,React团队给出过许多的方案,早期使用CreateClass + Mixins,在使用Class Component取代CreateClass之后又设计了Render Props和Higher Order Component,直到再后来的Function Component+ Hooks设计,React团队对于组件复用的探索一直没有停止
HOC使用(老生常谈)的问题:
嵌套地狱,每一次HOC调用都会产生一个组件实例
可以使用类装饰器缓解组件嵌套带来的可维护性问题,但装饰器本质上还是HOC
包裹太多层级之后,可能会带来props属性的覆盖问题
Render Props:
数据流向更直观了,子孙组件可以很明确地看到数据来源
但本质上Render Props是基于闭包实现的,大量地用于组件的复用将不可避免地引入了callback hell问题
丢失了组件的上下文,因此没有this.props属性,不能像HOC那样访问this.props.children
1.2.2 Javascript Class的缺陷
1、this的指向(语言缺陷)
class People extends Component {
state = {
name: ‘dm’,
age: 18,
}
handleClick(e) {
// 报错!
console.log(this.state);
}
render() {
const { name, age } = this.state;
return (
}
}
createClass不需要处理this的指向,到了Class Component稍微不慎就会出现因this的指向报错。
2、编译size(还有性能)问题:
// Class Component
class App extends Component {
state = {
count: 0
}
componentDidMount() {
console.log(‘Did mount!’);
}
increaseCount = () => {
this.setState({ count: this.state.count + 1 });
}
decreaseCount = () => {
this.setState({ count: this.state.count - 1 });
}
render() {
return (
<>
Counter
Increase
Decrease
</>
);
}
}
// Function Component
function App() {
const [ count, setCount ] = useState(0);
const increaseCount = () => setCount(count + 1);
const decreaseCount = () => setCount(count - 1);
useEffect(() => {
console.log(‘Did mount!’);
}, []);
return (
<>
Counter
Increase
Decrease
</>
);
}
Class Component编译结果(Webpack):
var App_App = function (_Component) {
Object(inherits[“a”])(App, _Component);
function App() {
var _getPrototypeOf2;
var _this;
Object(classCallCheck[“a”])(this, App);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = Object(possibleConstructorReturn[“a”])(this, (_getPrototypeOf2 = Object(getPrototypeOf[“a”])(App)).call.apply(_getPrototypeOf2, [this].concat(args)));
_this.state = {
count: 0
};
_this.increaseCount = function () {
_this.setState({
count: _this.state.count + 1
});
};
_this.decreaseCount = function () {
_this.setState({
count: _this.state.count - 1
});
};
return _this;
}
Object(createClass[“a”])(App, [{
key: “componentDidMount”,
value: function componentDidMount() {
console.log(‘Did mount!’);
}
}, {
key: “render”,
value: function render() {
return react_default.a.createElement(/…/);
}
}]);
return App;
}(react[“Component”]);
Function Component编译结果(Webpack):
function App() {
var _useState = Object(react[“useState”])(0),
_useState2 = Object(slicedToArray[“a” /* default */ ])(_useState, 2),
count = _useState2[0],
setCount = _useState2[1];
var increaseCount = function increaseCount() {
return setCount(count + 1);
};
var decreaseCount = function decreaseCount() {
return setCount(count - 1);
};
Object(react[“useEffect”])(function () {
console.log(‘Did mount!’);
}, []);
return react_default.a.createElement();
}
Javascript实现的类本身比较鸡肋,没有类似