【问题标题】:How to notify a child element that a job has finished?如何通知子元素作业已完成?
【发布时间】:2021-03-20 12:27:37
【问题描述】:

我有一个子元素,它显示从父元素传递的订单列表。我想在子级中放置一个“刷新”按钮,一旦按下该按钮,就会在父级内部调用一个函数来刷新订单。

单击刷新按钮时,其文本会通过使用本地状态挂钩转到“正在加载...”。问题是在新刷新的订单列表从父级到达后,我不确定如何将钩子的状态更改回“未加载”。

这是我的代码(也在 CodeSandbox:https://codesandbox.io/s/sad-wilbur-tbf7l?file=/src/App.js):

import React from "react";
import "./styles.css";

const OrderDisplay = ({ orders, refreshOrders }) => {
  const [refreshing, setRefreshing] = React.useState(false);

  const handleRefreshBtn = () => {
    setRefreshing(true);
    refreshOrders();
  };

  return (
    <div>
      <button onClick={handleRefreshBtn}>
        {refreshing ? <span>loading...</span> : <span>Refresh Orders</span>}
      </button>

      {orders.map((item, index) => (
        <div key={index}>
          Name: {item.name} | Total: ${item.total}
          <hr />
        </div>
      ))}
    </div>
  );
};

export default function App() {
  const [orders, setOrders] = React.useState(null);
  const initialMount = React.useRef(true);

  const getOrders = () => {
    setTimeout(() => {
      setOrders([
        { name: "Order 1", total: Math.floor(Math.random() * 10) + 1 },
        { name: "Order 2", total: Math.floor(Math.random() * 10) + 1 }
      ]);
    }, 1000);
  };

  if (initialMount.current) {
    initialMount.current = false;
    getOrders();
  }

  return (
    <div className="App">
      <h1>Orders</h1>
      {orders ? (
        <OrderDisplay orders={orders} refreshOrders={getOrders} />
      ) : null}
    </div>
  );
}

点击“刷新订单”按钮后,您会注意到订单刷新成功,但按钮停留在“正在加载...”。我该如何解决?

【问题讨论】:

    标签: javascript reactjs react-hooks


    【解决方案1】:

    一旦订单发生变化,您可以将setRefreshing 设置为 false,如下所示:

    import React,{useEffect} from "react";
    import "./styles.css";
    
    const OrderDisplay = ({ orders, refreshOrders }) => {
      const [refreshing, setRefreshing] = React.useState(false);
    
      const handleRefreshBtn = () => {
        setRefreshing(true);
        refreshOrders();
      };
    useEffect(()=>{
      if(orders.length)
      {
        setRefreshing(false)
      }
    
    },[orders]);
    
      return (
        <div>
          <button onClick={handleRefreshBtn}>
            {refreshing ? <span>loading...</span> : <span>Refresh Orders</span>}
          </button>
    
          {orders.map((item, index) => (
            <div key={index}>
              Name: {item.name} | Total: ${item.total}
              <hr />
            </div>
          ))}
        </div>
      );
    };
    
    export default function App() {
      const [orders, setOrders] = React.useState(null);
      const initialMount = React.useRef(true);
    
      const getOrders = () => {
        setTimeout(() => {
          setOrders([
            { name: "Order 1", total: Math.floor(Math.random() * 10) + 1 },
            { name: "Order 2", total: Math.floor(Math.random() * 10) + 1 }
          ]);
        }, 1000);
      };
    
      if (initialMount.current) {
        initialMount.current = false;
        getOrders();
      }
    
      return (
        <div className="App">
          <h1>Orders</h1>
          {orders ? (
            <OrderDisplay orders={orders} refreshOrders={getOrders} />
          ) : null}
        </div>
      );
    }
    

    但是如果订单长度变为零,这将不起作用。在这种情况下,您可以删除 useEffect 中的 if 条件

    【讨论】:

      【解决方案2】:

      你可以做这样的事情。在代码中读取 cmets

      import React from "react";
      import "./styles.css";
      
      const OrderDisplay = ({ orders, refreshOrders }) => {
        const [refreshing, setRefreshing] = React.useState(false);
      
        const handleRefreshBtn = () => {
          setRefreshing(true);
          //
          //
          // Here you pass your setRefreshing
          // method to getOrders
          refreshOrders(setRefreshing);
        };
      
        return (
          <div>
            <button onClick={handleRefreshBtn}>
              {refreshing ? <span>loading...</span> : <span>Refresh Orders</span>}
            </button>
      
            {orders.map((item, index) => (
              <div key={index}>
                Name: {item.name} | Total: ${item.total}
                <hr />
              </div>
            ))}
          </div>
        );
      };
      
      export default function App() {
        const [orders, setOrders] = React.useState(null);
        const initialMount = React.useRef(true);
      
        //
        //
        // Here you defining it
        const getOrders = (setRefreshing) => {
          setTimeout(() => {
            setOrders([
              { name: "Order 1", total: Math.floor(Math.random() * 10) + 1 },
              { name: "Order 2", total: Math.floor(Math.random() * 10) + 1 }
            ]);
            //
            //
            // Here you use it
            setRefreshing(false);
          }, 1000);
        };
      
        if (initialMount.current) {
          initialMount.current = false;
          getOrders();
        }
      
        return (
          <div className="App">
            <h1>Orders</h1>
            {orders ? (
              <OrderDisplay orders={orders} refreshOrders={getOrders} />
            ) : null}
          </div>
        );
      }
      

      【讨论】:

      • 没有考虑过这样做。恐怕在我的具体实现中,这将需要太多的更改。我认为useEffect 解决方案主要回答了我的问题。感谢您的帮助。
      【解决方案3】:

      App 组件应该将是否刷新订单传递给OrderDisplay 一个。因为它持有那个状态。所以试试这个方法:

      import React from 'react';
      import './styles.css';
      
      const OrderDisplay = ({ orders, refreshOrders, isRefreshing }) => {
          const handleRefreshBtn = () => {
              refreshOrders();
          };
      
          return (
              <div>
                  <button onClick={handleRefreshBtn}>
                      {isRefreshing ? (
                          <span>loading...</span>
                      ) : (
                          <span>Refresh Orders</span>
                      )}
                  </button>
      
                  {orders.map((item, index) => (
                      <div key={index}>
                          Name: {item.name} | Total: ${item.total}
                          <hr />
                      </div>
                  ))}
              </div>
          );
      };
      
      export default function App() {
          const [orders, setOrders] = React.useState(null);
          const [refreshing, setRefreshing] = React.useState(false);
      
          // you do not need it see comment above React.useEffect
          // const initialMount = React.useRef(true);
      
          const getOrders = () => {
              setRefreshing(true)
      
              setTimeout(() => {
                  setOrders([
                      { name: 'Order 1', total: Math.floor(Math.random() * 10) + 1 },
                      { name: 'Order 2', total: Math.floor(Math.random() * 10) + 1 },
                  ]);
      
                  setRefreshing(false);
              }, 1000);
          };
          // useEffect should be used instead of useRef if you need to fetch data
          // pass an empty array as a useEffect dependencies and it will ensures that
          // useEffect will be called only once after initial render
          React.useEffect(() => {
              getOrders();
          }, []);
          //   if (initialMount.current) {
          //     initialMount.current = false;
          //     getOrders();
          //   }
      
          return (
              <div className="App">
                  <h1>Orders</h1>
                  {orders ? (
                      <OrderDisplay orders={orders} refreshOrders={getOrders} isRefreshing={refreshing} />
                  ) : null}
              </div>
          );
      }
      

      【讨论】:

      • 只是好奇,您为什么不在子元素内的 orders 变量上使用 useEffect 来检测更改?换句话说,你为什么不按照@Sakshi 的建议去做?
      • @codemonkey 如果你的第一次订单是 { name: 'Order 1', total: 3, { name: 'Order 2', total:4} ,第二次也是一样(假设)然后第二次 useEffect 不会出现在图片中,因为 useEffect 只有在订单发生变化时才会起作用。因此第二次刷新值不会设置为 false。所以如果你确定订单每次点击刷新按钮都会改变,那么 useEffect 方法很好。
      • @codemonkey 因为可能会出现订单列表刷新但列表中没有订单的情况。
      • @OleksandrSakun 因为可以删除 useEffect 中存在的 if 条件
      • @Sakshi 但这不是真的。我只是在每次刷新时都将订单列表设为相同:codesandbox.io/s/sad-wilbur-tbf7l?file=/src/App.js 它仍然像魅力一样工作。你能解释一下吗?
      猜你喜欢
      • 2018-12-07
      • 1970-01-01
      • 2019-09-06
      • 1970-01-01
      • 2016-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多