我通过自己的一个快速项目开始学习 React 和 Recoil。一个具有按钮的单页网站,简而言之,每个按钮都会进行 REST 调用,并使用返回的数据创建一个表。
单击按钮时,它会更新一个存储给定按钮的 URL 的原子。选择器订阅此原子并触发对 API 的异步 REST 请求。从那里,返回的数据被处理成一个表。
在第一次单击每个按钮时,一切都按预期工作。调用特定的 URL 并生成表。问题是,如果第二次单击按钮,则会显示先前获取的数据。我不确定我是否遗漏了一个重要步骤,并且调试并没有真正向我透露任何信息。
原子类:
import {atom} from 'recoil'
export const currentTableState = atom({
key: 'currentTableState',
default: 'Employee',
})
export const currentUrlState = atom({
key: 'currentUrlState',
default: '/employee/find/all?page=0&size=12'
})
export const currentPageState = atom({
key: 'currentPageState',
default: 0
})
export const currentSizeState = atom({
key: 'currentSizeState',
default: 12
})
选择器类:
import {selector} from "recoil";
import {currentUrlState} from "./atoms";
export const tableData = selector({
key: "currentContactDetails",
get: async ({get}) => {
const response = await fetch(String(get(currentUrlState)));
const json = await response.json();
return json.content;
}
});
包含按钮的 NavBar 类:
import React from "react";
import './topNavBar.css';
import {useSetRecoilState, useRecoilValue} from "recoil";
import {currentPageState, currentSizeState, currentTableState, currentUrlState} from "../recoil/atoms";
const TopNavBar = () => {
const setCurrentTable = useSetRecoilState(currentTableState);
const setUrl = useSetRecoilState(currentUrlState);
const page = useRecoilValue(currentPageState);
const size = useRecoilValue(currentSizeState);
function updateState(table) {
setCurrentTable(table);
if (table.includes(" ")) {
let split = table.split(" ");
table = split[1] + '/' + split[0];
}
setUrl(`${table.toLowerCase()}/find/all?page=${page}&size=${size}`);
}
return (
<div className='top-nav'>
<button onClick={() => updateState("Employee")}>Employee
</button>
<button onClick={() => updateState("Material")}>Material
</button>
<button onClick={() => updateState("Equipment")}>Equipment
</button>
<button onClick={() => updateState("Material Request")}>Material Request
</button>
<button onClick={() => updateState("Equipment Request")}>Equipment Request
</button>
</div>
)
}
export default TopNavBar;
生成表格的表格类:
import React from "react";
import './table.css'
import {useRecoilState, useRecoilValueLoadable} from "recoil";
import {currentTableState} from "../recoil/atoms";
import {tableData} from "../recoil/selectors";
const Table = () => {
const [currentTable] = useRecoilState(currentTableState);
const items = useRecoilValueLoadable(tableData);
const Data = () => {
switch (items.state) {
case 'loading':
return <div>Loading...</div>;
case 'hasError':
throw items.contents;
case 'hasValue':
return <ConstructTable items={items.contents}/>
default:
return <div>Something went terribly wrong.</div>
}
}
const TableHeaders = (headers) => <tr>{headers.headers.map(key => <th key={key.toString()}>{key}</th>)}</tr>;
const TableRows = (items) =>
items.items.map(row =>
<tr className='body' key={row.id}>
{Object.values(row).map((cell, cellIndex) => <td key={cellIndex}>{cell}</td>)}
</tr>
)
const ConstructTable = (content) => {
let {items} = content
if (items.length === 0) return <div>{currentTable} table has no data...</div>
else return (
<div className="table">
<table>
<thead>
<tr>
<th
className="table-name"
colSpan={Object.keys(items[0]).length}
>
{currentTable}
</th>
</tr>
<TableHeaders headers={Object.keys(items[0])}/>
</thead>
<tbody>
<TableRows items={items}/>
</tbody>
</table>
</div>
);
}
return <Data/>
}
export default Table;
更新:我通过放弃使用选择器并混合钩子和原子来获得类似的功能来解决这个问题,如下所示:
const url = useRecoilValue(currentUrlState);
const table = useRecoilValue(currentTableState);
const [data, setData] = useRecoilState(currentTableData);
const [requestState, setRequestState] = useState('loading');
useEffect(() => {
console.debug('use effect triggered')
setRequestState('loading');
fetch(String(url))
.then(res => res.json())
.then(
(result) => {
setData(result.content);
setRequestState('loaded');
},
(error) => setRequestState(error.message)
);
}, [url, setData])
const Data = () => {
switch (requestState) {
case 'loading':
return <div>Loading...</div>;
case 'loaded':
return <ConstructTable items={data}/>
default:
return <div>{requestState}</div>
}
}
虽然我不觉得这很理想。