使用反应状态作为信号可以吗?[关闭]

Is it ok to use react states as signals? [closed]

提问人:AntoninL 提问时间:10/17/2023 最后编辑:AntoninL 更新时间:10/17/2023 访问量:97

问:


想改进这个问题吗?更新问题,以便可以通过编辑这篇文章用事实和引文来回答。

上个月关闭。

我有一个代码盯着一些正在修改我的 React 状态的异步函数。但问题是,在解决所有承诺后启动其他函数时,该函数没有我的状态的更新版本:

const [myState, setMyState] = useState<myType[]>({...});

const pendingPromises: Promise<void>[] = [];

async function asyncFunction(a: myType){
  return fs.promises.copyFile(a.currentPath, a.destination).then(() => {
    setMyState((myState) => [...myState.filter((b) => (b.id != a.id)), {...a, curentPath: a.destination}]

function saveMyState(){
  // Actually just saving it to a JSON
  console.log(myState) // This shows the original state before it has been modified by asyncFunction()
}

function mainFunction(){
  myArray.forEach((a) => pendingPromises.push(asyncFunction(a))

  Promise.all(pendingPromises).then(saveMyState)
}

因此,为了解决这个问题,我声明了一个新状态,当我需要调用第二个函数时,我会将其更改为 true:

const [myState, setMyState] = useState<myType[]>({...});

const [callFunction, setCallFunction] = useState<boolean>(false);

const pendingPromises: Promise<void>[] = [];

useEffect(() => {
  if (callFunction) {
    saveMyState();
    setCallFunction(false);
  }
}, [callFunction]);

async function asyncFunction(a: myType){
  return fs.promises.copyFile(a.currentPath, a.destination).then(() => {
    setMyState((myState) => [...myState.filter((b) => (b.id != a.id)), {...a, curentPath: a.destination}]
  });

function saveMyState(){
  // Actually just saving it to a JSON file
  console.log(myState) // This shows the original state before it has been modified by asyncFunction()
}

function mainFunction(){
  myState.forEach((a) => pendingPromises.push(moveFile(a))

  Promise.all(pendingPromises).then(() => setCallFunction(true)) // calling function
}

这很有效,但感觉不是很干净。这种方式可以吗?如果没有,我应该如何进行?

javascript reactjs 异步 react-hooks

评论

0赞 Robert Harvey 10/17/2023
定义“确定”。
1赞 T.J. Crowder 10/17/2023
另一个问题是,如果此代码是组件的主体,则为空心。它不应在组件的顶层声明。如果你在渲染之间需要它,它应该是 state 或 ref 或类似的东西。如果没有,它应该在需要它的函数内。一个适当的最小可重现示例将真正帮助我们帮助您,理想情况下,使用堆栈片段(工具栏按钮)的可运行示例。Stack Snippets 支持 React,包括 JSX;这是如何做的。pendingPromises[<>]
1赞 Nick Parsons 10/17/2023
我假设你在里面调用是因为你想在每个异步操作完成后重新渲染?否则,如果你只关心最终结果并渲染它,那么与其在内部调用,不如积累你在其中获得的所有异步值,并使用传递给回调的数组结果来构建你的最终状态值(然后调用)在那里。setMyState()asyncFunctionsetMyState()asyncFunctionmainFunction()doStuffWithUpdatedStatesetMyState()
1赞 AntoninL 10/17/2023
@T.J.Crowder:不,它没有
2赞 T.J. Crowder 10/17/2023
@AntoninL - 感谢您更新问题。我已经更新了我的答案,更改为 ,并添加一个新的(第一个)选项,现在我们知道该函数的作用。:-)(理想情况下,将来最好不要在现有答案使用函数时更改函数的名称。但再次感谢您提供更多信息,以及通常回答我们的问题!doStuffWithUpdatedStatesaveMyState

答:

2赞 T.J. Crowder 10/17/2023 #1

通常在这种情况下,所讨论的函数正在更改状态,因此使用状态 setter 的回调形式是简单的解决方案。但不会改变状态,它只是使用它。saveMyState

在这种情况下,最好不要通过设置该状态标志来调用函数来导致重新呈现。

以下是一些替代方案:

  1. 由于 的工作是将当前状态保存为 JSON,因此您可以考虑在状态更改时执行此操作,而不是让 JSON 版本一段时间内与状态保持不同步。要做到这一点,根本不需要调用它,只需在以下位置进行以下工作:saveMyStatePromise.all(/*...*/).thenuseEffect

    useEffect(() => {
        // ...save `myState` as JSON...
    }, [myState]); // <== Triggers the effect callback when `myState` changes
    
  2. 无论如何,您可以继续使用状态 setter 的回调形式,只需返回它收到的相同状态,使状态更新调用不执行任何操作:

    function saveMyState(){
        setMyState((state) => {
            // ...save `state` as JSON...
            return state; // Return state unchanged
        });
    }
    

    我已经这样做了几次,但从来没有真正满意过。:-)

  3. 您可以使用 to always have access to up-to-date state:ref

    const [myState, setMyState] = useState<myType>({/*...*/});
    const refMyState = useRef(myState); // Using the initial value here just
                                        // makes the types easier
    refMyState.current = myState;       // Make sure the ref is always up-to-date
                                        // (You *could* put the update in a
                                        // `useEffect`, but that's needlessly
                                        // complex.)
    
    // ...
    
    function saveMyState(){
        const state = refMyState.current;
        // ...save `state` as JSON...
    }
    
  4. 使用其他机制来存储,而不是 ,例如 Redux(这样做是一个足够大的变化,我不会做一个例子,他们的文档已经涵盖了它。myStateuseState

-1赞 Steve Tomlin 10/17/2023 #2

信号的好处是,无论你有多少反应子项,只有那些监听信号的人才会重新渲染。包含上述组件的所有内容,无论是上下文还是 useRef,其所有链都将在任何更改时重新呈现。

如果你想创建信号 - 在窗口对象上创建一个观察者,任何 react 组件都可以监听。类似于 javascript 代理的东西。下面是一个示例:

https://github.com/inspiraller/react-proxy