微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

当原子状态更新为先前值时,React Recoil选择器不会触发其获取

如何解决当原子状态更新为先前值时,React Recoil选择器不会触发其获取

我已经通过做一个自己的快速项目开始学习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>
        }
    }

尽管我不认为这是理想的。

解决方法

好吧,我根据您的第一个版本创建了一个沙箱。它似乎工作正常。 注意:仅启用员工和物料按钮。表格数据也与您的不同。但它应该说明这个想法。

https://codesandbox.io/s/friendly-wiles-kjpjw?file=/src/Table.js

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。