Why Hooks? Uncovering the Functional Foundations of React and Beyond
This article explores the motivations behind React's Hooks API, contrasts it with the Class API, delves into functional programming concepts, demonstrates implementations via Mixins, HOCs, and custom hooks, and compares React Hooks to Vue's Composition API, offering deep insights for front‑end developers.
Why Hooks API?
Hooks API has been visible to developers since 2018, yet many still misunderstand it. The article asks why we should replace the Class API with Hooks, notes the learning curve, and points out common pitfalls such as closure traps.
TL;DR
Differences between Hooks API and Class API
Functional programming in Hooks API
Hooks API and codata/Algebra
React Hooks vs Vue Composition API
Functional Programming
The core of Hooks API is functional programming: most APIs are expressed as functions. JSX is a function that maps state to a virtual DOM, and setState triggers re‑rendering. Redux also follows functional principles, using reducers and actions to produce immutable state updates.
interface State {
count: number;
}
const state: State = { count: 0 };
const INCR = (state: State, payload: number = 1): State => ({
count: state.count + payload
});
const DECR = (state: State, payload: number = 1): State => ({
count: state.count - payload
});Redux’s state management records a sequence of actions, allowing time‑travel debugging, but it can be conceptually heavy for those unfamiliar with functional programming.
Logic Abstraction Methods
Mixins
Mixins use React.createClass to inject logic into components, but they are limited and prone to conflicts.
import React from "react";
import ReactDOM from "react-dom";
import { Row, Col, Button } from "antd";
const counterMixin = {
getInitialState() { return { count: 0 }; },
incr(increment = 1) { this.setState({ count: this.state.count + increment }); },
decr(decrement = 1) { this.setState({ count: this.state.count - decrement }); }
};
// Deprecated since React 15.5.0
const Counter = React.createClass({
mixins: [counterMixin],
render() {
return (
<Row style={{ width: 150, textAlign: "center" }}>
<Col span={24}><h3>{this.state.count}</h3></Col>
<Col span={12}><Button onClick={() => this.incr()}>INCR</Button></Col>
<Col span={12}><Button onClick={() => this.decr()}>DECR</Button></Col>
</Row>
);
}
});Higher‑Order Component (HOC)
HOCs wrap a component and pass additional props, offering more flexibility than mixins.
import React, { Component } from "react";
import ReactDOM from "react-dom";
import { Row, Col, Button } from "antd";
function CounterComponent(WrappedComponent) {
return class extends Component {
state = { count: 0 };
incr(increment = 1) { this.setState({ count: this.state.count + increment }); }
decr(decrement = 1) { this.setState({ count: this.state.count - decrement }); }
render() {
const countProps = { count: this.state.count, incr: this.incr, decr: this.decr };
return <WrappedComponent {...this.props} {...countProps} />;
}
};
}
const Counter = CounterComponent(props => {
const { count, incr, decr } = props;
return (
<Row style={{ width: 150, textAlign: "center" }}>
<Col span={24}><h3>{count}</h3></Col>
<Col span={12}><Button onClick={incr}>INCR</Button></Col>
<Col span={12}><Button onClick={decr}>DECR</Button></Col>
</Row>
);
});Hooks API
Hooks enable function components to hold state. The article provides a custom hook useCount and a demo counter.
import React, { useState } from "react";
import ReactDOM from "react-dom";
import { Row, Col, Button } from "antd";
function useCount(initCount = 0) {
const [count, setCount] = useState(initCount);
const incr = () => setCount(count + 1);
const decr = () => setCount(count - 1);
return { count, incr, decr };
}
function Counter() {
const { count, incr, decr } = useCount();
return (
<Row style={{ width: 150, textAlign: "center" }}>
<Col span={24}><h3>{count}</h3></Col>
<Col span={12}><Button onClick={incr}>INCR</Button></Col>
<Col span={12}><Button onClick={decr}>DECR</Button></Col>
</Row>
);
}The article notes that async/await does not give the updated state inside Hooks, unlike Class API where await this.setState works.
TypeScript‑Driven Functional Programming
Two basic function types are introduced: one where input and output share the same type, and another where they differ (mapping). The article shows how to infer types and use them to model data flow, referencing Lodash as an example.
"I don't know the data, it's a parameter"
Illustrated with Lodash functions and data‑flow diagrams, emphasizing that the data shape is unknown while the processing logic is fixed.
"I don't know the behavior, it's a parameter"
Describes abstracting async actions (e.g., useRequest) and queuing behavior ( runTrack) so that callers remain unaware of the underlying orchestration.
const { run: fetchPosts, loading } = wrapRequest(authorId => fetchPostsService(authorId));
fetchPosts(123);
const statusText = loading ? "Loading..." : "Done";Codata and Algebra
Codata is presented as a familiar mathematical concept (algebra). The article uses the circle‑area formula S = πr² to illustrate unknown parameters (r, π) and how they become concrete only at render time.
It explains composition (building a function from smaller parts) and visit (rendering) as analogous to Hook execution, and why Hooks must be called unconditionally to keep the parameter set stable.
React Hooks vs Vue Composition API
Vue 3 introduces a Composition API similar to React Hooks. The article shows a Vue example using ref and setup, highlighting differences such as single‑file components and separate compose/render phases.
import Vue from "vue";
import VueCompositionAPI, { ref } from "@vue/composition-api";
import Antd from "ant-design-vue";
Vue.use(VueCompositionAPI);
Vue.use(Antd);
const template = `
<a-row id="app">
<a-col :span="24"><h3>{{count}}</h3></a-col>
<a-col :span="12"><a-button @click="() => incr()">INCR</a-button></a-col>
<a-col :span="12"><a-button @click="() => decr()">DECR</a-button></a-col>
</a-row>`;
new Vue({
name: "App",
template,
setup() {
const count = ref(0);
const incr = (increment = 1) => (count.value += increment);
const decr = (decrement = 1) => (count.value -= decrement);
return { count, incr, decr };
}
}).$mount("#app");Vue’s compose step runs once, while React re‑executes the Hook body on every state change, affecting performance tuning.
Play Together?
The article mentions projects that blend React and Vue concepts, such as reactivue, demonstrating a component defined with both libraries.
import React from "react";
import { defineComponent, ref } from "reactivue";
import { Row, Col, Button } from "antd";
const Counter = defineComponent(() => {
const count = ref(0);
const incr = (increment = 1) => (count.value += increment);
const decr = (decrement = 1) => (count.value -= decrement);
return { count, incr, decr };
}, ({ count, incr, decr }) => (
<Row style={{ width: 150, textAlign: "center" }}>
<Col span={24}><h3>{count}</h3></Col>
<Col span={12}><Button onClick={incr}>INCR</Button></Col>
<Col span={12}><Button onClick={decr}>DECR</Button></Col>
</Row>
));Conclusion
The author encourages readers to rethink Hooks from a design‑pattern perspective rather than only reading source code, emphasizing that understanding the functional foundations and type‑driven abstractions can lower the learning curve and inspire new ways to use React.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
