Frontend Development 22 min read

React Hooks: From Mixins and HOCs to Custom Hook Implementations and Production Practices

This article explains the evolution of React component reuse—from legacy mixins and higher‑order components to modern React Hooks—provides step‑by‑step implementations of custom hooks such as useLogTime, useFetchHook, useInterval and useImgLazy, and demonstrates how to apply them in real‑world production code for better performance and maintainability.

Ctrip Technology
Ctrip Technology
Ctrip Technology
React Hooks: From Mixins and HOCs to Custom Hook Implementations and Production Practices

The author, a former front‑end engineer at Ctrip’s overseas ride‑hailing team, shares a comprehensive tutorial on React component reuse and the transition to React Hooks.

1. Before React Hooks

React’s component model enables Lego‑like UI composition, but as business logic grows, mixing lifecycle methods with business code leads to duplication and maintenance pain. The article first revisits two older reuse patterns.

1.1 React.mixin

Mixins were used with React.createClass to merge shared methods into a component’s prototype. They are now discouraged because they are hard to trace and hurt performance. Example:

var logMixin = {
    alertLog: function(){
        alert('alert mixin...');
    },
    componentDidMount: function(){
        console.log('mixin did mount');
    }
};

var MixinComponentDemo = React.createClass({
    mixins: [logMixin],
    componentDidMount: function(){
        document.body.addEventListener('click',()=>{ this.alertLog(); });
        console.log('component did mount');
    }
});
// console output:
// component did mount
// mixin did mount
// (click page) alert mixin

1.2 Higher‑Order Components (HOC)

HOCs are functions that take a component and return a new component, allowing logic such as logging and timing to be shared without inheritance. Example implementation:

function logTimeHOC(WrappedComponent, options = {time:true, log:true}){
    return class extends React.Component {
        constructor(props){
            super(props);
            this.state = {index:0};
            this.show = 0;
        }
        componentDidMount(){
            options.time && (this.timer = setInterval(()=>{ this.setState({index: ++index}); },1000));
            options.log && console.log('组件渲染完成----');
        }
        componentDidUpdate(){
            options.log && console.log(`我背更新了${++this.show}`);
        }
        componentWillUnmount(){
            this.timer && clearInterval(this.timer);
            options.log && console.log('组件即将卸载----');
        }
        render(){
            return
;
        }
    };
}

Using the HOC, three concrete components are created (LogComponent, SetTimeComponent, LogTimeShowComponent) by wrapping simple presentational components.

2. React Hooks

Hooks solve the same reuse problems while allowing function components and eliminating the need for this . A custom hook useLogTime is introduced to encapsulate logging and timing logic:

import React, { useState, useEffect } from 'react';
function useLogTime(data = {log:true, time:true}){
    const [count, setCount] = useState(0);
    useEffect(() => {
        data.log && console.log('组件渲染完成----');
        let timer = null;
        if(data.time){
            timer = setInterval(() => setCount(c=>c+1), 1000);
        }
        return () => {
            data.log && console.log('组件即将卸载----');
            data.time && clearInterval(timer);
        };
    }, []);
    return {count};
}

The article then walks through building a minimal useState and useEffect from scratch to illustrate how hooks work internally, showing the need for a global memoizedState array and a currentCursor to keep state isolated per hook call.

let memoizedState = [];
let currentCursor = 0;
function useState(initVal){
    memoizedState[currentCursor] = memoizedState[currentCursor] || initVal;
    const setVal = newVal => { memoizedState[currentCursor] = newVal; render(); };
    return [memoizedState[currentCursor++], setVal];
}
function useEffect(fn, watch){
    const hasWatchChange = memoizedState[currentCursor]
        ? !watch.every((val,i)=> val===memoizedState[currentCursor][i])
        : true;
    if(hasWatchChange){
        fn();
        memoizedState[currentCursor] = watch;
        currentCursor++;
    }
}

Diagrams (omitted) illustrate how hook calls are stored sequentially in the array, which explains the rule that hooks must be called at the top level of a component.

3. React in Production

The article maps class lifecycle methods to Hook equivalents (e.g., componentDidMount → useEffect(()=>{},[]) ) and shows common patterns such as data fetching with a custom useFetchHook that returns {data, status} and handles loading, success, and error states.

function useFetchHook(config, watch){
    const [data, setData] = useState(null);
    const [status, setStatus] = useState(0); // 0 loading, 1 success, 2 error
    useEffect(() => {
        const fetchData = async () => {
            try {
                const result = await axios(config);
                setData(result.data);
                setStatus(1);
            } catch(err){
                setStatus(2);
            }
        };
        fetchData();
    }, watch ? [watch] : []);
    return {data, status};
}

Performance tips include using useCallback or useMemo to avoid recreating functions on each render, and a custom useInterval hook that abstracts setInterval with proper cleanup:

function useInterval(callback, time=300){
    const intervalFn = useRef();
    useEffect(() => { intervalFn.current = callback; });
    useEffect(() => {
        const timer = setInterval(() => { intervalFn.current(); }, time);
        return () => clearInterval(timer);
    }, [time]);
}

Finally, a lazy‑loading image hook useImgLazy is presented, which checks if images are within the viewport using getBoundingClientRect and loads the real source when needed.

function useImgLazy(className){
    useEffect(() => {
        const onScroll = () => checkImgs(className);
        window.addEventListener('scroll', onScroll);
        checkImgs(className);
        return () => window.removeEventListener('scroll', onScroll);
    }, []);
}

The article concludes with a list of reference links to official React documentation, deep‑dive articles on Hook internals, and several community posts about best practices.

FrontendJavaScriptReactHooksuseEffectHOCCustomHook
Ctrip Technology
Written by

Ctrip Technology

Official Ctrip Technology account, sharing and discussing growth.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.