对父组件的异步函数调用

时间:2019-10-26 18:02:17

标签: javascript reactjs redux react-redux

我有这个

const Parent = () => {
    [modalOpen, setModal] = React.useState(false);

    return <Child
               open={modalOpen}
               closeModal={() => setModal(false)}
               functionFromParent={() => console.log('Logged')} />
}

const Child = ({ functionFromStore, functionFromParent, closeModal }) => {
    async function foo() {
        try {
          await ...;
          functionFromStore();
          functionFromParent();
        } catch (error) {
          ....
        }
    }

    const bar = () => {
        foo();
        closeModal();
    }

    return <div
           style={{backgroundColor: 'hotpink', width: '10rem', height: '10rem' }}
           onClick={() => bar()}/>
}

组件。

<Parent />决定是否显示<Child />(它是 Modal )。 <Child />具有三个功能,closeModal()functionFromParent(),它们来自<Parent/ >functionFromStore()reduxdispatchToProps()。为了简单起见,我忽略了所有connect(stateToProps, dispatchToProps)(...)的内容。但是,假设<Child />直接连接到store

单击<div />中的<Child/>将执行bar()。这导致<Child />unmount,因为closeModal()中的<Parent />被调用,从而关闭了 modal 。但是,bar()也调用foo(),它是一个async函数。

解析await后,将调用functionFromStore(),但不会调用functionFromParent()。我不知道为什么呢?为什么即使卸载了组件(store却调用了<Child />的函数,却不是来自父级的函数呢?

此外,即使卸载了functionFromParent(),也可以调用<Child />吗?它可以与functionFromStore()一起使用,是否有办法使其与functionFromParent()一起使用?

3 个答案:

答案 0 :(得分:1)

我无法重现您描述的行为,但是它可能是closure problem,因为您要多次设置状态,但是在处理程序的关闭状态中有过时的状态值

您可以通过将回调传递给状态设置器setSomeState(currentState=>({...currentState,changes}))来解决,这是一个完整的示例:

function Parent() {
  const [state, setState] = React.useState({
    showModal: true,
    showFoo: false,
  });

  return (
    <div>
      {state.showModal ? (
        <Child
          open={state.showModal}
          closeModal={() =>
            setState(state => ({
              ...state,
              showModal: false,
            }))
          }
          functionFromParent={() =>
            setState(state => ({
              ...state,
              showFoo: true,
            }))
          }
        />
      ) : (
        'Child is gone '
      )}
      {state.showFoo ? <Foo /> : 'no foo'}
    </div>
  );
}
function Child({ functionFromParent, closeModal }) {
  function foo() {
    setTimeout(() => {
      functionFromParent();
    }, 1000);
  }
  const bar = () => {
    foo();
    closeModal();
  };
  return <button onClick={bar}>click me</button>;
}
function Foo() {
  return 'hi, I am Foo';
}
ReactDOM.render(
  <Parent />,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

答案 1 :(得分:1)

functionFromParent必须已被调用。肯定还有其他导致问题的原因。

const Parent = () => {
  const [showChild, setShowChild] = React.useState(true);
  const childProps = {
    fnFromParent: () => {
      console.log("logging from fnFromParent");
    },
    unmount: () => {
      setShowChild(false);
    }
  };
  return (
    <div>
      <p>I'm a Parent</p>
      { showChild &&
        <Child {...childProps} />
      }
    </div>
  );
};

const Child = ({ fnFromParent, unmount }) => {
  const delayed = () => {
    const p = new Promise(res => setTimeout(res, 3000))
    p.then(() => fnFromParent());
  };
  const clickHandler = () => {
    delayed();
    unmount();
  };
  return <p onClick={clickHandler}>I'm a Child (click to remove me)</p>;
};

ReactDOM.render(<Parent />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

答案 2 :(得分:-1)

function Parent() {
  const [modalOpen, setModal] = React.useState(false);

  return (
    <Child
      open={modalOpen}
      closeModal={() => setModal(false)}
      functionFromParent={() => console.log("Logged")}
    />
  );
}
  

是否有一种方法可以调用functionFromParent(),即使已卸载也是如此?它以某种方式与functionFromStore()一起工作,是否有办法使其与functionFromParent()一起工作?

  • 是!。卸载组件时,您可以执行React钩子的useEffect提供的清理。单击 div 后,将调用下面的代码,即调用 props.closeModal ,这将卸载您的 Child 组件。卸载组件后,useEffect然后执行useEffect的return块中的清理工作,该清理工作将调用functionFromParentfunctionFromStore
function Child(props) {
  useEffect(() => {
    return () => {
      props.functionFromStore();
      props.functionFromParent();
    };
  });

  return (
    <div
      style={{ backgroundColor: "hotpink", width: "10rem", height: "10rem" }}
      onClick={props.closeModal}
    />
  );
}