首页 > 解决方案 > 在 Redux 的 reducer 中,我可以在全局(模块)范围内设置一个变量吗?

问题描述

这是使用 ReactJS、Redux 和 Redux-thunk。

在里面reducer/get-data.js,我可以设置一个

let dataFetchingStartTimestamp;

在全球范围内?我想在全局范围内设置它的原因是函数getData()以块的形式获取数据,所以第一次,假设它获取前 300 条记录(假设 300 条记录可能需要 10 秒),第二次,另外 300 条记录,依此类推,直到达到 6000 条记录。getData()AJAX 成功后会调用自己。(then承诺的处理程序)。

当用户更改 UI Dropbox 时,现有getData()循环应该停止并使用新值重新启动,并且之前发生的 AJAX 不应该将数据置于 Redux 状态。所以这就是为什么当我们有 时dataFetchingStartTimestamp,我们可以设置它,当它是第一次获取数据时,dataFetchingStartTimestamp设置为Date.now(),然后如果用户更改 UI,那么它将是另一个“第一次获取数据” ” 并因此改变dataFetchingStartTimestamp. 所以在这个函数内部或者reducer里面,如果它看到那个dataFetchingStartTimestamp和之前的值不一样,那么它什么也不会做。这意味着,只会执行最新的dataFetchingStartTimestamp操作。

我想要传递这个值的方式是,getData()在初始 AJAX 之后调用自身时,将 传递dataFetchingStartTimestamp给自身, as dataFetchingContinuationTimestamp,所以这个循环将有一个dataFetchingContinuationTimestamp与 global 进行比较的值dataFetchingStartTimestamp

reducer 是如何得到这个值的:它会在第一次的时候,和 redux 状态的 payload 一起getData()分派一个 a 。所以在reducer中,后面的语句可以检查当前全局是否和Redux状态一样。如果它们不同,则根本不要改变状态。GET_DATA_STARTdataFetchingStartTimestampcasedataFetchingStartTimestamp

可以做到这一点,或者也许真的有更好的方法来处理它?我们也可以使用

const dataFetchingStartTimestamp = { value: null };

如果我们只允许设置一个常量,但value将作为全局值。

另一种方法是制作(函数对象)dataFetchingStartTimestamp的属性。getData

也许这里所谓的 globaldataFetchingStartTimestamp并不是真正的全局,而是 webpack 打包的作用域的局部变量,或者作为模块内部的局部变量?

标签: reactjsreduxredux-thunk

解决方案


我希望我能正确理解这个问题,用户输入会导致异步操作调度,所以如果用户输入搜索输入字符a,然后b会触发 2 个搜索:aab. 如果他们以与用户提供输入不同的顺序解决怎么办?ab搜索在 1 秒后解析并设置状态。搜索在a1 秒后解析并覆盖ab. 您的 UI 现在不同步并显示ab在搜索文本中,但搜索结果为a(请参阅下面的隐藏示例)

const later = (value) =>
  new Promise((resolve) =>
    setTimeout(
      () => resolve(value),
      value.length === 1 ? 2000 : 1000
    )
  );
const asyncFunction = (setter, value) => {
  console.log('starting async with value:', value);
  later(value).then((value) => {
    console.log('resolved with value:', value);
    setter(value);
  });
};
const App = () => {
  const [result, setResult] = React.useState('');
  const onChange = ({ target: { value } }) => {
    asyncFunction(setResult, value);
  };
  return (
    <div>
      <input type="text" onChange={onChange} />
      <h4>Result:{result}</h4>
    </div>
  );
};

ReactDOM.render(<App />, 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>

以下是解决此问题的方法:

const later = (value) =>
  new Promise((resolve) =>
    setTimeout(
      () => resolve(value),
      value.length === 1 ? 2000 : 1000
    )
  );
const REPLACED = { message: 'REPLACED' };
const resolveLast = (fn) => {
  const check = {};
  return (...args) => {
    const current = {};
    check.current = current;
    //You could make check.current Data.now() but
    //  object reference is guaranteed to be unique
    return Promise.resolve(fn(...args)).then((resolve) =>
      check.current === current
        ? resolve
        : Promise.reject(REPLACED)
    );
  };
};
const lastLater = resolveLast(later);

const asyncFunction = (setter, value) => {
  console.log('starting async with value:', value);
  lastLater(value)
    .then((value) => {
      console.log('resolved with value:', value);
      setter(value);
    })
    .catch((err) =>
      err === REPLACED
        ? console.log(
            `request for ${value} replaced with newer request`
          )
        : Promise.reject(err)
    );
};
const App = () => {
  const [result, setResult] = React.useState('');
  const onChange = ({ target: { value } }) => {
    asyncFunction(setResult, value);
  };
  return (
    <div>
      <input type="text" onChange={onChange} />
      <h4>Result:{result}</h4>
    </div>
  );
};

ReactDOM.render(<App />, 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>


推荐阅读