如何使用 React 钩子将状态从子组件传递到父组件?

时间:2021-04-03 15:37:26

标签: javascript reactjs react-hooks

我有如下的父组件。这里我想在子组件里面设置setLoading函数 加载中。

const ReportRechargeDetail = React.memo(() => {
  const [selectedDates, setSelectedDates] = useState([]);
const [loading, setLoading] = useState(false);
  const [isOpen, setIsOpen] = useState(false);
return (
    <>
       <Row className="header-wrapper">
        <div>
          Select date range to proceed : <Space />
          <RangePicker
            format="YYYY-MM-DD"
            onChange={onChange}
            disabled={loading}
            onOpenChange={onOpenChange}
            disabledDate={(currentDate) => currentDate.isAfter(moment())}

          />

        </div>
      </Row>
      { selectedDates ? (
        <Row className="header-wrapper">
          <div>
            <PrimeButton
              buttonName={"Download report"}
              onClick={onClickHeaderButton}
            />
          </div>
        </Row>
      ) : (<div></div>)
      }
      <Tabs defaultActiveKey="1" onChange={callback} type="card">
        <TabPane tab="IN APP" key="1">
          <GenerateReport
            columns={columns}
            reportType={"recharge-info/in-app"}
            reportName={"Recharge Details "}
            breadcrumbName={"RECHARGED_DETAILED_REPORT"}
            fileDownloadUrl={""}
            selectedDateRange={selectedDates}
            isOpen={isOpen}
            isLoadingCallBack={setLoading}
          />
        </TabPane>
        <TabPane tab="BALANCE" key="2">
          <GenerateReport
            columns={columns}
            reportType={"recharge-info/online"}
            reportName={"Recharge Details "}
            breadcrumbName={"RECHARGED_DETAILED_REPORT"}
            fileDownloadUrl={"Recharge"}
            selectedDateRange={selectedDates}
            isOpen={isOpen}
          />
        </TabPane>
      </Tabs>

    </>
  );
});

export default ReportRechargeDetail;


子组件如下。但我收到一个错误,错误:无效的钩子调用。钩子只能在函数组件的主体内部调用。这可能是由于以下原因之一造成的:

  1. 您的 React 和渲染器版本可能不匹配(例如 React DOM)
  2. 你可能违反了钩子规则
  3. 您可能在同一个应用中拥有多个 React 副本
const GenerateReport = React.memo(
    ({
        columns,
        reportType,
        reportName,
        breadcrumbName,
        fileDownloadUrl,
        downloadReport,
        downloadReportName,
        selectedDateRange,
        isOpen,
        isLoadingCallBack
    }) => {
        const [show, setShow] = useState(false);
        //const [isOpen, setIsOpen] = useState(false);
        const [pageNumber, setPageNumber] = useState(1);
        const [pageSize, setPageSize] = useState(10);
        const [selectedDates, setSelectedDates] = useState(selectedDateRange);
        const [performedActionList, setPerformedActionList] = useState([]);
        const [totalCount, SetTotalCount] = useState(0);
        const [loading, setLoading] = useState(false);
        const [requiredTotal, setRequiredTotal] = useState(true);
        const dispatch = useDispatch();

 const getRecurrentData = (dateStrings) => {
            isLoadingCallBack(true);
            setLoading(true);
            setShow(true);
 });
        };

return (
            <>
                {show ? (
                    <Spin spinning={loading}>
                        <ViewBoxBody
                            loading={loading}
                            showPrimaryButton={false}
                            columns={columns}
                            filterSearchBar={false}
                            dataSource={performedActionList}
                            onPaginationClick={onPaginationClick}
                            currentPage={pageNumber}
                            totalDataCount={totalCount}
                            pageSize={pageSize}
                            showSizeChanger={true}
                            onShowSizeChange={(curent, size) => setPageSize(size)}
                            hideOnSinglePage={false}
                            showTotal={(total) => {
                                return `Total ${total} items`;
                            }}
                        />
                        <ViewBoxFooter

                            onPaginationClick={onPaginationClick}
                            currentPage={pageNumber}
                            totalDataCount={totalCount}
                            pageSize={pageSize}
                            showSizeChanger={true}
                            onShowSizeChange={(curent, size) => setPageSize(size)}
                            hideOnSinglePage={false}
                            showTotal={(total) => {
                                return `Total ${total} items`;
                            }}
                        />
                    </Spin>
                ) : (
                    <div></div>
                )}
            </>
        );
    }
);

2 个答案:

答案 0 :(得分:0)

不要将 setLoading 传递给子组件,而是将钩子调用包装在父组件中用户定义的方法中。

...
const toggleLoading = (value) => {
  setLoading(value)
}
...
return ...

并在子组件的 props isLoadingCallBack={toggleLoading} 中使用该方法。根据复杂性,将父状态包装在 Context [1] 中是有意义的,以便您可以访问子树中任何位置的值/设置器。

[1] https://reactjs.org/docs/context.html

答案 1 :(得分:0)

此错误有错误描述中列出的 3 个原因

<块引用>
  1. 您的 React 和渲染器版本可能不匹配(例如 React DOM)

  2. 你可能违反了钩子规则

  3. 您可能在同一个应用中拥有多个 React 副本

让我们先拿数字 2 来确保我们遵循钩子的规则

您的代码看起来不错,但让我们使用一小段工作代码,我们确信可以在您的环境中试用它https://codesandbox.io/s/little-monad-0jo71?file=/src/App.js 试试这里的示例,如果有效,请将其放在单独的页面/路由中那么问题就出在您的代码上,我们将对其进行更多调查。

现在排名第一

<块引用>

您的 React 和渲染器版本可能不匹配(例如 反应DOM)

建议的解决方案:

仔细检查你的 react 和 react-dom 版本

现在排名第三

<块引用>

您可能在同一个应用中拥有多个 React 副本

建议的解决方案:

试试这个问题的解决方案https://github.com/facebook/react/issues/13991