Javascript useEffect与redux道具更新

Javascript useEffect与redux道具更新,javascript,reactjs,Javascript,Reactjs,我有一个可以在一个页面上多次使用的组件。它所做的是进行一个外部调用,并将该外部调用中的值保存到键对象中的redux存储。我只希望组件执行一次,所以我使用的是componentDidMount。现在,如果在页面上再次使用相同的组件,我不希望它再次执行外部调用。这在使用类时可以正常工作,但当我尝试使用函数挂钩时,它就不再工作了。 让我首先向您展示基于类的代码 class MyComponent extends Component { componentDidMount() { setTi

我有一个可以在一个页面上多次使用的组件。它所做的是进行一个外部调用,并将该外部调用中的值保存到键对象中的redux存储。我只希望组件执行一次,所以我使用的是componentDidMount。现在,如果在页面上再次使用相同的组件,我不希望它再次执行外部调用。这在使用类时可以正常工作,但当我尝试使用函数挂钩时,它就不再工作了。 让我首先向您展示基于类的代码

class MyComponent extends Component {
  componentDidMount() {
    setTimeout(() => this.wait(), 0);
  }

  wait() {
    const { key, map } = this.props;
    if (map[key] === undefined) {
      saveKey(key);
      console.log('LOAD EXTERNAL ID ONLY ONCE');
      externalAPI(this.externalHandler.bind(this));
    }
  }

  externalHandler(value) {
    const { key, setValue } = this.props;
    setValue(key, value);
  }

  render() {
    const { key, map children } = this.props;
    return (
      <>
        {children}
      </>
    );
  }
} 

mapStateToProps .....

export default connect(mapStateToProps, { saveKey, setValue })(MyComponent);
Page.js 调用每个组件,如下所示

import React from 'react';

const Page = () => {
  return (
    <>
      <MyComponent key='xxxxx'>First Component</MyComponent>
      <MyComponent key='xxxxx'>Second Component</MyComponent>
    </>
  );
};

export default Page;
以上所有工作。因此,当第一个组件挂载时,我延迟了对redux的调用,不知道为什么会这样,但确实如此。有人能告诉我为什么使用setTimeout有效吗???而不使用setTimeout则不会。我指的是超时,第一个组件挂载设置键,因为map[key]==未定义。第二个组件挂载映射[key]不再是未定义的。但是如果没有超时映射,[key]总是===未定义

它将传递的密钥属性存储在redux中。第二个组件装载并看到存储了相同的密钥,因此它不需要再次调用外部API getExternalID。如果第三个组件安装了不同的密钥,那么它应该再次运行外部API调用,依此类推

正如我所说的,以上所有方法都有效,但我不确定为什么我需要做一个设置来让它工作

第二个问题是将其转换为函数和挂钩,而不是类。由于某些原因,这不起作用

import React, { useEffect } from 'react';

const MyComponent = ({ children, key, map, saveKey, setValue }) => {

  useEffect(() => {
    setTimeout(() => delay(), 0);
  }, [map[key]]);

  const delay = () => {
    if (map[key] === undefined) {
      saveKey(key);
      console.log('LOAD VARIANT ONLY ONCE');
      externalAPI(externalHandler);
    }
  };

  const externalHandler = (value) => {
    setValue(key, value);
  };


  return (
      <>
        {children}
      </>
  );
};

export default MyComponent;
第一个问题:

Javascript只使用一个线程,因此即使使用延迟0毫秒,也会在React的render方法退出后调用该方法。这是一个我相信可以解释的实验:

function log(msg){
   $("#output").append("<br/>"+msg);
}
function render(){
  log("In render");
  log("Delayed by 0 ms without setTimeout...")
  setTimeout(() =>log("Delayed by 0 ms with setTimeout..."), 0);
  for(var i = 0;i<10;i++) log("Delay inside render "+i);
  log("Render finish");
}
render();

但这是一个有点糟糕的分享参考。因此,更好的设计可能是使用缓存。不要通过地图,而是一个能手和一个二传手。getKey和saveKey。这些方法的底层可能会使用映射来持久化已设置的键。

谢谢semako。我认为componentDidMount是React,意思是“React的渲染方法exit”与React的第一次渲染相同,您可以安全地使用dom或其他任何东西。在我的例子中,您的组件呈现为调用外部api。第二个问题。saveKey是一个动作,您可以从我的代码中看到这一点。导出默认ConnectMapStateTops,{saveKey,setValue}MyComponent;map是redux store的道具,从mapStateToProps获取。这就是我在开场白中提到的对象键。它是Redux中存储密钥的对象。可以在我的Reducer代码中看到。是的,componentDidMount是调用外部API的好地方。但是,如果您在设置的超时时间内执行此操作,则会发生与react lifecycle不同步的情况。在这种情况下,最好的办法可能是从组件中删除API调用,并将其放入一个缩减器中。。。
function log(msg){
   $("#output").append("<br/>"+msg);
}
function render(){
  log("In render");
  log("Delayed by 0 ms without setTimeout...")
  setTimeout(() =>log("Delayed by 0 ms with setTimeout..."), 0);
  for(var i = 0;i<10;i++) log("Delay inside render "+i);
  log("Render finish");
}
render();
if (map[key] === undefined) {
      map[key]=true;
      saveKey(key);
      console.log('LOAD VARIANT ONLY ONCE');
      externalAPI(externalHandler);
    }