Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Reactjs 如何处理类组件中的竞争条件?_Reactjs - Fatal编程技术网

Reactjs 如何处理类组件中的竞争条件?

Reactjs 如何处理类组件中的竞争条件?,reactjs,Reactjs,假设有一个组件,其中要求服务器执行一些搜索并提供响应。如何确保即使服务器端出于任何原因以不同的顺序应答,也能提供最新请求的响应?我不是要取消以前的请求,因为经过合理的努力,这并不总是可能的 onClick = () => { apiCall(this.state.searchQuery).then(items => this.setState({ items })); }; 有没有优雅的处理方法?到目前为止,我知道的方法很少: 在请求到来之前禁用按钮(在大量情况下提供不好

假设有一个组件,其中要求服务器执行一些搜索并提供响应。如何确保即使服务器端出于任何原因以不同的顺序应答,也能提供最新请求的响应?我不是要取消以前的请求,因为经过合理的努力,这并不总是可能的

 onClick = () => {
   apiCall(this.state.searchQuery).then(items => this.setState({ items }));
 };
有没有优雅的处理方法?到目前为止,我知道的方法很少:

  • 在请求到来之前禁用按钮(在大量情况下提供不好的体验-例如在键入时搜索)
  • 检查内部
    then()
    如果请求的参数与
    this.props
    /
    this.state
    数据匹配(当我们故意用相同的查询强制进行新搜索时,不处理这种情况-比如按Enter/单击“搜索”按钮)
  • 以某种方式标记请求并检查它是否是最新的(有效,但看起来过于冗长,特别是当我们需要检查的请求很少时)
  • 我会称那三人组为“丑陋、破碎和混乱”

    是否有钩子允许的清晰方式:

     useEffect(() => {
       const isCanceled = false;
       apiCall(searchQuery).then(items => !isCanceled && setItems(items));
       return () => {isCanceled = true;};
     }, [searchQuery])
    

    您的onClick处理程序建议类组件,因为您使用了
    this
    this.setState

    onClick = () => {
      apiCall(this.state.searchQuery).then(items =>
        this.setState({ items })
      );
    };
    
    我调整了
    onlyLastRequestedPromise
    以获取一个函数,该函数将返回某些内容(您可以返回Promise.reject('cancelled')或任何内容)

    关于如何使用它的课堂示例:

    class Component extends React.Component {
      CANCELLED = {};
      last = onlyLastRequestedPromise(
        'search',
        () => this.CANCELLED
      );
      onSearch = () => {
        this.last(apiCall(this.state.searchQuery)).then(
          items =>
            items !== this.CANCELLED && this.setState({ items })
        );
      };
      changeAndSearch = e => {
        this.setState(
          {}, //state with new value
          () => this.onSearch() //onSearch after state update
        );
      };
      render() {
        return (
          <div>
            <SearchButton onClick={this.onSearch} />
            <Other onChange={this.changeAndSearch} />
          </div>
        );
      }
    }
    

    最终从hooks的世界中找到了如何利用闭包来模仿“忽略那个”方法:

    class MyComponent extends React.Component {
      const ignorePrevRequest = () => {}; // empty function by default
    
      loadSomeData() {
        this.ignorePrevRequest(); 
        let cancelled = false;
        this.ignorePrevRequest = () => { cancelled = true; }; // closure comes to play
        doSomeCall().then(data => !cancelled && this.setState({ data }))
      }
    }
    

    所以你;I’我希望承诺仅在用户最近一次单击时解决?类似于?@HMR,是的,类似于我的#3,但搬到了外部助手/图书馆。你能知道其他选择吗?当然,它可以工作,但看起来相当复杂。它会清理您的第三个选项,看起来非常像useEffect,
    last
    可以是您的类的属性:
    last=onlyLastRequestedPromise(“search”)
    并且在promise或任何您调用api的地方:
    this.last(apiCall(this.state.searchQuery))。然后(items=>items!==CANCELLED&&this.setState({items}));
    只有
    onlyLastRequestedPromise
    才能用一个名为CANCELLED的常量进行解析。实际上,我的意思是
    searchQueryIndex
    是类属性,但我明白了,它不清楚。你能用你的建议运行官方答案吗?“您的onClick处理程序建议类组件”实际上是在标题中:)@skyboyer是的,你是对的,我被useEffect钩子弄糊涂了,它看起来更干净,但在渲染时会做一些事情,而不会在单击时做一些事情。目标是找出如何获得类组件的干净方法,就像
    useEffect
    所允许的那样。你认为我最好重写这个问题,让它更清楚吗?@skyboyer不,我想大多数人都能理解。如果你有功能组件,你可以把last作为一个定制的钩子来编写:
    const last=useLast(apiCall);
    而onSearch就是:
    last(searchQuery)。然后(result=>对result做点什么)
    构建最后一个和挂载的签入useLast钩子。
    const onlyLastRequestedPromise = (promiseIds => {
      const whenResolve = (
        promise,
        id,
        promiseID,
        resolveValue,
        whenCancelled = () => Promise.reject('cancelled')
      ) => {
        if (promise !== undefined) {
          //called by user adding a promise
          promiseIds[id] = {};
        } else {
          //called because promise is resolved
          return promiseID === promiseIds[id]
            ? Promise.resolve(resolveValue)
            : whenCancelled(resolveValue);
        }
        return (function(currentPromiseID) {
          return promise.then(function(result) {
            return whenResolve(
              undefined,
              id,
              currentPromiseID,
              result
            );
          });
        })(promiseIds[id]);
      };
      return (id = 'general', whenCancelled) => promise =>
        whenResolve(
          promise,
          id,
          undefined,
          undefined,
          whenCancelled
        );
    })({});
    
    class Component extends React.Component {
      CANCELLED = {};
      last = onlyLastRequestedPromise(
        'search',
        () => this.CANCELLED
      );
      onSearch = () => {
        this.last(apiCall(this.state.searchQuery)).then(
          items =>
            items !== this.CANCELLED && this.setState({ items })
        );
      };
      changeAndSearch = e => {
        this.setState(
          {}, //state with new value
          () => this.onSearch() //onSearch after state update
        );
      };
      render() {
        return (
          <div>
            <SearchButton onClick={this.onSearch} />
            <Other onChange={this.changeAndSearch} />
          </div>
        );
      }
    }
    
    //
    function ComponentContainer(props) {
      const CANCELLED = useRef({});
      const last = useRef(
        onlyLastRequestedPromise('search', () => CANCELLED)
      );
      const [searchQuery,setSearchQuery] = useState({});
      const mounted = useIsMounted();
      const onSearch = useCallback(
        last(apiCall(searchQuery)).then(
          items =>
            items !== CANCELLED &&
            mounted.current &&
            //do something with items
        )
      );
    }
    
    class MyComponent extends React.Component {
      const ignorePrevRequest = () => {}; // empty function by default
    
      loadSomeData() {
        this.ignorePrevRequest(); 
        let cancelled = false;
        this.ignorePrevRequest = () => { cancelled = true; }; // closure comes to play
        doSomeCall().then(data => !cancelled && this.setState({ data }))
      }
    }