如何在React中用class和hooks分别实现useFetch

618 阅读1分钟

背景

之前面大厂遇到这个面试题, 现在有空理一下思路, 顺下写下我的实现

hooks版本

思路: 使用自定义hooks抽取状态

const defaultState = {
    status: 'idle', // 状态值: 初始 | 加载中 | 错误 | 成功
    data: null,
    error: null,
}

const useFetch = (initialState) => {
    const [state, setState] = useState({
        ...defaultState,
        ...initialState,
    });
    
    const setData = (data) => {
        setState({
            data,
            status: 'success',
            error: null,
        })
    };
    
    const setError = (error) => {
         setState({
            error,
            status: 'error',
            data: null,
        })
    };
    
    // 这里run传入的参数为promise(调取接口的方法)
    const run = (promise) => {
        if(!promise || !promise.then) {
            throw new Error('请传入Promise类型')
        }
        setState({ ...state, status: 'loading' })
        
        return promise.then(data => {
            setData(data)
            return data;
        }).catch(err => {
            setError(err)
            return err
        })
    }
    
    return {
        isIdle: state.status === 'idle',
        isLoading: state.status === 'loading',
        isError: state.status === 'error',
        isSuccess: state.status === 'success',
        run,
        setData,
        setError,
        ...state,
    }
}

class版本

思路:利用高阶组件的render props component(增强组件props)

class Fetcher extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            data: null,
            status: 'idle'
            error: null
        }
    }
    
    componentDidMount() {
        this.setState({ status: 'loading' })
        fetch(this.props.url) // children组件传进来的url
            .then(res => {
                this.setState({
                    data: res,
                    status: 'success',
                    error: null,
                })
            })
            .catch(error => {
                this.setState({
                    error,
                    status: 'error',
                    data: null
                })
            })
    }
    
    render() {
        const renderProps = {
            isIdle: this.state.status === 'idle',
            isLoading: this.state.status === 'loading',
            isError: this.state.status === 'error',
            isSuccess: this.state.status === 'success',
            ...this.state,
        }
        return this.props.children(renderProps)
    }
}

使用

const MyComponent = () => {
    <Fetcher url={xxxxxxx}>
        {({ data, error, isLoading, isError, ...rest }) => {
            if(isError) {
                return <div>{error}</div>
            }
            if(isLoading) {
                return <div>loading...</div>
            }
            if(!data) {
                return <div>No Data</div>
            }
            
            return (
                <div>{data}</div>
            )
        }
       }
    </Fetcher>
}