如何在ReasonReact应用中从API获取数据?
在ReasonReact中从API获取数据并渲染
嘿,我完全懂你的困惑——ReasonReact的官方文档确实在API请求这块讲得比较简略,我当初刚上手的时候也找了好久才摸清楚门道。其实实现起来并不复杂,咱们一步步来拆解:
核心思路
在ReasonReact中获取API数据,本质上和React逻辑一致:
- 用**状态(state)**管理加载状态、返回数据和错误信息
- 直接调用JS原生的
fetchAPI发起请求(Reason可以无缝调用JS API) - 借助
Js.Promise处理异步逻辑,请求完成后更新组件状态 - 根据状态渲染对应的UI(加载中、数据展示、错误提示)
完整代码示例(函数组件 + Hooks)
1. 定义状态与数据类型
首先用Reason的静态类型系统约束数据结构,保证类型安全:
// 定义API返回的单条数据类型 type post = { id: int, title: string, }; // 定义组件状态类型 type state = { loading: bool, data: option<array<post>>, // option表示可能无数据 error: option<string>, // option表示可能无错误 };
2. 初始化状态并发起请求
用React.useState初始化状态,在React.useEffect0中触发异步请求:
[@react.component] let make = () => { let (state, setState) = React.useState(() => { loading: true, data: None, error: None, }); // 组件挂载时发起API请求 React.useEffect0(() => { Js.Promise.( // 调用JS原生fetch请求数据 fetch("https://jsonplaceholder.typicode.com/posts") // 解析响应为JSON格式 |> then_(response => response->json()) // 将动态JSON转换为Reason静态类型 |> then_(json => { let posts = json->Js.Json.decodeArray->Belt.Option.map(arr => arr->Belt.Array.map(item => { // 处理可能的类型解析失败,给默认值兜底 id: item->Js.Json.decodeInt->Belt.Option.getWithDefault(0), title: item->Js.Json.decodeString->Belt.Option.getWithDefault(""), }) ); // 更新状态:加载完成,存入解析后的数据 setState(_prev => {loading: false, data: posts, error: None}); resolve(); }) // 捕获请求过程中的错误 |> catch(error => { setState(_prev => {loading: false, data: None, error: Some(error->Js.String.make)}); resolve(); }) // 忽略Promise返回值(useEffect不需要返回Promise) |> ignore ); None; // 无清理函数,返回None }); // 根据状态渲染对应UI switch (state.loading, state.data, state.error) { | (true, _, _) => <div> {React.string("Loading...")} </div> | (false, Some(posts), None) => <ul> {posts->Belt.Array.map(post => <li key={string_of_int(post.id)}> {React.string(post.title)} </li> )->React.array} </ul> | (false, None, Some(error)) => <div> {React.string("请求出错: " ++ error)} </div> | (false, None, None) => <div> {React.string("暂无数据")} </div> } };
关键细节说明
- JSON类型转换:JS的JSON是动态类型,Reason是静态类型,必须用
Js.Json模块的解析函数(如decodeArray、decodeInt)转换类型,并用Belt.Option处理解析失败的场景。 - Promise处理:Reason的
Js.Promise和JS Promise用法几乎一致,then_和catch链式处理异步逻辑,resolve()用来结束当前Promise链。 - 不可变状态:Reason的记录是不可变的,更新状态时要返回完整的新记录,不能只修改单个字段。
类组件写法(兼容旧版本)
如果习惯用类组件,可以在componentDidMount生命周期中发起请求:
type post = {id: int, title: string}; type state = {loading: bool, data: option<array<post>>, error: option<string>}; class component extends React.Component(. props) { initialState() { {loading: true, data: None, error: None} } componentDidMount() { Js.Promise.( fetch("https://jsonplaceholder.typicode.com/posts") |> then_(response => response->json()) |> then_(json => { let posts = json->Js.Json.decodeArray->Belt.Option.map(arr => arr->Belt.Array.map(item => { id: item->Js.Json.decodeInt->Belt.Option.getWithDefault(0), title: item->Js.Json.decodeString->Belt.Option.getWithDefault(""), }) ); this.setState({loading: false, data: posts, error: None}); resolve(); }) |> catch(error => { this.setState({loading: false, data: None, error: Some(error->Js.String.make)}); resolve(); }) |> ignore ); } render() { // 复用之前的状态判断渲染逻辑 switch (this.state.loading, this.state.data, this.state.error) { // ... 省略渲染代码 } } }
这样应该就能顺利实现API数据的获取和渲染了。如果遇到特定的类型转换或者异步处理问题,随时调整细节就行!
内容的提问来源于stack exchange,提问作者Mateus Felipe
相关产品推荐
相关产品推荐

