如何在 React 组件中包装方法
How do I wrap methods in react components
我有很多函数看起来像这样
doSomething = async (...) => {
try {
this.setState({loading: true});
...
var result = await Backend.post(...);
...
this.setState({loading: false});
} catch(err) {
this.setState({error: err});
}
}
基本上我有 2 个变量加载和错误,我必须为很多函数管理这些变量,并且代码对于所有这些函数基本上是相同的。由于 javascript 中没有装饰器,而且我不希望为此安装任何实验性库,我如何包装此函数以从上面删除重复的 setStates ?
这是我目前的方式,我将函数作为参数传递。
我们有很多 API,从后端获取数据,我们必须处理错误并对数据做一些事情。
只是服务的数据不同,处理错误是一样的
private processServiceResponse(resp: any, doSthWithData: (data: any) => void) {
let { errors } = resp;
if (this.hasError(errors)) {
this.handleServiceErr(errors);
return;
}
let data = resp;
if (resp && resp.data) {
data = resp.data;
}
doSthWithData(data);
}
这是我将函数作为参数传递的方式。
let rest1 = service1.getData();
processServiceResponse(rest1,(data)=>{
//only need to focus with processing data.
})
PS: 打字稿编码。
您可以使用高阶函数(将另一个函数作为参数的函数)使常见的加载和错误功能可重用。它非常类似于装饰器模式。例如:
const doSomething = withLoadingAndErrorHandling(Backend.post, this.setState);
function withLoadingAndErrorHandling(fn, setState) {
return async function(...args) {
try {
setState({loading: true});
var result = await fn(args);
setState({loading: false});
return result;
} catch(err) {
setState({error: err});
}
}
}
如果你使用的是函数组件,你可以自定义一个钩子来避免重复代码
//useFetch.js
import { useState, useEffect } from 'react';
import axios from 'axios';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
setLoading('loading...')
setData(null);
setError(null);
const source = axios.CancelToken.source();
axios.get(url, { cancelToken: source.token })
.then(res => {
setLoading(false);
//checking for multiple responses for more flexibility
//with the url we send in.
res.data.content && setData(res.data.content);
res.content && setData(res.content);
})
.catch(err => {
setLoading(false)
setError('An error occurred. Awkward..')
})
return () => {
source.cancel();
}
}, [url])
return { data, loading, error }
export default useFetch;
用法:
import useFetch from './useFetch';
import './App.css';
function App() {
const { data: quote, loading, error } =
useFetch('https://api.quotable.io/random')
return (
<div className="App">
{ loading && <p>{loading}</p> }
{ quote && <p>"{quote}"</p> }
{ error && <p>{error}</p> }
</div>
);
}
export default App;
我有很多函数看起来像这样
doSomething = async (...) => {
try {
this.setState({loading: true});
...
var result = await Backend.post(...);
...
this.setState({loading: false});
} catch(err) {
this.setState({error: err});
}
}
基本上我有 2 个变量加载和错误,我必须为很多函数管理这些变量,并且代码对于所有这些函数基本上是相同的。由于 javascript 中没有装饰器,而且我不希望为此安装任何实验性库,我如何包装此函数以从上面删除重复的 setStates ?
这是我目前的方式,我将函数作为参数传递。
我们有很多 API,从后端获取数据,我们必须处理错误并对数据做一些事情。
只是服务的数据不同,处理错误是一样的
private processServiceResponse(resp: any, doSthWithData: (data: any) => void) {
let { errors } = resp;
if (this.hasError(errors)) {
this.handleServiceErr(errors);
return;
}
let data = resp;
if (resp && resp.data) {
data = resp.data;
}
doSthWithData(data);
}
这是我将函数作为参数传递的方式。
let rest1 = service1.getData();
processServiceResponse(rest1,(data)=>{
//only need to focus with processing data.
})
PS: 打字稿编码。
您可以使用高阶函数(将另一个函数作为参数的函数)使常见的加载和错误功能可重用。它非常类似于装饰器模式。例如:
const doSomething = withLoadingAndErrorHandling(Backend.post, this.setState);
function withLoadingAndErrorHandling(fn, setState) {
return async function(...args) {
try {
setState({loading: true});
var result = await fn(args);
setState({loading: false});
return result;
} catch(err) {
setState({error: err});
}
}
}
如果你使用的是函数组件,你可以自定义一个钩子来避免重复代码
//useFetch.js
import { useState, useEffect } from 'react';
import axios from 'axios';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
setLoading('loading...')
setData(null);
setError(null);
const source = axios.CancelToken.source();
axios.get(url, { cancelToken: source.token })
.then(res => {
setLoading(false);
//checking for multiple responses for more flexibility
//with the url we send in.
res.data.content && setData(res.data.content);
res.content && setData(res.content);
})
.catch(err => {
setLoading(false)
setError('An error occurred. Awkward..')
})
return () => {
source.cancel();
}
}, [url])
return { data, loading, error }
export default useFetch;
用法:
import useFetch from './useFetch';
import './App.css';
function App() {
const { data: quote, loading, error } =
useFetch('https://api.quotable.io/random')
return (
<div className="App">
{ loading && <p>{loading}</p> }
{ quote && <p>"{quote}"</p> }
{ error && <p>{error}</p> }
</div>
);
}
export default App;