概要
レスポンスデータとloadingのステートをセットにして表現すると良さそうではないですか!という提案の記事です
// typescriptです
enum Status {
loading = 'loading',
success = 'success',
failure = 'failure',
}
interface IResult<T> {
status: Status
response: T | Error | null
}
interface IHit {
objectID: string
url: string
title: string
}
interface IState {
hits: IResult<IHit[]>
}
class App extends Component<{}, IState> {
componentDidMount() {
this.setState({
hits: {
status: Status.loading,
hits: null
}
});
fetch(API + DEFAULT_QUERY)
.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error('Something went wrong ...');
}
})
.then(data => this.setState({ hits: data.hits, isLoading: false }))
.catch(error => this.setState({ hits: error, isLoading: false }));
}
render() {
const { hits } = this.state;
if (hits.status === Status.failure) {
return <p>{hits.response.message}</p>;
}
if (hits.status === Status.loading) {
return <p>Loading ...</p>;
}
return (
<ul>
{hits.response.map(hit =>
<li key={hit.objectID}>
<a href={hit.url}>{hit.title}</a>
</li>
)}
</ul>
);
}
}
背景
普通は下記のような方法で表現することが多いかなと思います
参考: https://medium.com/@ghengeveld/async-data-loading-in-react-94661e23cd3d
class App extends Component {
componentDidMount() {
this.setState({ isLoading: true });
fetch(API + DEFAULT_QUERY)
.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error('Something went wrong ...');
}
})
.then(data => this.setState({ hits: data.hits, isLoading: false }))
.catch(error => this.setState({ error, isLoading: false }));
}
render() {
const { hits, isLoading, error } = this.state;
if (error) {
return <p>{error.message}</p>;
}
if (isLoading) {
return <p>Loading ...</p>;
}
return (
<ul>
{hits.map(hit =>
<li key={hit.objectID}>
<a href={hit.url}>{hit.title}</a>
</li>
)}
</ul>
);
}
}
これだけ見るとあんまり違いはないのですが、fetchするデータの種類が増えてくると isLoadingHits
, isLoadingUsers
みたいに管理すべきフラグが増えて複雑になってしまうなと感じていました
そこで私は下記のような書き方することもあったのですが、わかりにくいしあんまりよくないなと感じていました
interface State {
// undefined: 未取得
// null: 取得失敗したとき
// IHit[]: 取得成功したとき
hits: IHit[] | undefined | null
}
提案しているIResult
はswiftを真似ています
// SearchRepositoriesRequest conforms to Request protocol.
let request = SearchRepositoriesRequest(query: "swift")
// Session receives an instance of a type that conforms to Request.
Session.send(request) { result in
switch result {
case .success(let response):
// Type of `response` is `[Repository]`,
// which is inferred from `SearchRepositoriesRequest`.
print(response)
case .failure(let error):
self.printError(error)
}
}
swiftだと↑こんな感じでレスポンスの結果をenumで表現する文化があるのですが、これが結構わかりやすくて好きで、同じ事をやりたいなというモチベーションです
typescriptの型システム的に値付きenumっぽい表現ができなさそうだったので、ちょっと妥協して今回のような型になっています
(本当はこういう風に表現したかった)
redux
この例ではreactのstateにデータを保持していますが、reduxのstoreでも全く同じ要領でやれるかなと思っています
今後
個人のPJで試しにやってみているので、コードが公開できるようになったら、実際にどんな感じか感想を書こうと思います