简体   繁体   English

在 history.listen 的 useEffect 中使用 useState 钩子

[英]Using useState hook in useEffect on history.listen

I am having some troubles with useState when listening to history changes in useEffect .在收听useEffecthistory变化时,我在使用useState时遇到了一些问题。

When the pathname changes, setState is initiated but then the state is added back.pathname更改时,会启动setState ,但随后会添加回state

For example, I have a flag component that collects groups of notifications, but on the pathname change, I want all flags to be dismissed and removed from state .例如,我有一个收集通知组的flag组件,但是在pathname更改时,我希望所有标志都被解除并从state中删除。

The flag component标志组件

const PageFlag = ({ history }: InterfaceProps) => {
const { contextData, dismissFlag, dismissAllFlags } = useContext(GlobalConsumer);

  useEffect(() => {
    history.listen(() => {
      dismissAllFlags();
    });
  });

  return (
    <>
      <FlagGroup onDismissed={dismissFlag}>
        {contextData.flags.map((flag, index) => (
          <Flag key={index} {...flag} />
        ))}
      </FlagGroup>
    </>
  );
};

History prop is used from import { withRouter } from 'react-router-dom'历史道具用于import { withRouter } from 'react-router-dom'

The state and function for dismissAllFlags is shown in a createContext component as用于dismissAllFlags的 state 和 function 在createContext组件中显示为

const DefaultState: InterfaceState = {
  otherStateExample: false,
  flags: []
};

export const GlobalConsumer = createContext({
  contextData: DefaultState,
  addFlag: (flagData: any) => {},
  dismissFlag: () => {},
  dismissAllFlags: () => {}
});

export const GlobalProvider = ({ children }: InterfaceProps) => {
  const [state, setState] = useState<InterfaceState>({
    ...DefaultState
  });

  return (
    <GlobalConsumer.Provider
      value={{
        contextData: state,
        addFlag: (flagData: any) => {
          setState({ ...state, flags: [flagData].concat(state.flags) });
        },
        dismissFlag: () => {
          setState({ ...state, flags: state.flags.slice(1) });
        },
        dismissAllFlags: () => {
          setState({ ...state, flags: [] });
        }
      }}
    >
      {children}
    </GlobalConsumer.Provider>
  );
};

The problem arises, where on pathname change, dismissAllFlags uses setState to set flags as [] but then adds back the previous state with the flags .问题出现了,在pathname更改时, dismissAllFlags使用setStateflags设置为[]但随后将先前的 state 与flags添加回来。

How can I remove all flags but remember the current state for other items ?如何删除所有flags但记住其他items的当前state

You are missing the second input parameter on useEffect() , which is going to cause the listener to be readded on every render.您缺少useEffect()上的第二个输入参数,这将导致在每次渲染时都读取侦听器。

It should look like this, note you also should not need the inner function.它应该看起来像这样,注意你也不应该需要内部 function。

useEffect(() => {
  history.listen(dismissAllFlags)
}, []);

We use it like this:我们像这样使用它:

const [isOpen, setIsOpen] = useState(false);
useEffect(() => {
    history.listen(() => setIsOpen(false));
  }, [history]);

If I understand what your asking is this:如果我明白你的要求是这样的:

You would like to set the flags to an empty array without have to add the previous values using the spread method that you are currently doing {...state, flags: []} .您想将标志设置为空数组,而不必使用您当前正在执行的扩展方法添加先前的值{...state, flags: []}

Well, this is not possible using useState and you should be cautions with nested state objects as cloning can becoming expensive with larger objects.好吧,使用useState是不可能的,您应该注意嵌套的 state 对象,因为使用较大的对象进行克隆可能会变得昂贵。 Maybe that is what your trying to avoid here .也许这就是你在这里试图避免的

Even if you switch to useReducer you would still end up prop spreading for the state.即使您切换到useReducer ,您最终仍会为 state 进行道具传播。

Perhaps you should just have flags as its own state const [flags, setFlags] = useState([]) or look into immutable-helper .也许您应该将标志作为自己的 state const [flags, setFlags] = useState([])或查看immutable-helper

Moreover, Try to respect react-hooks/exhaustive-deps otherwives funny things can start happening.此外,尽量尊重react-hooks/exhaustive-deps otherwives 有趣的事情可能会开始发生。 If no deps then at least give your hook an empty array to ensure it is performed once.如果没有部门,那么至少给你的钩子一个空数组以确保它执行一次。

I tried to reproduce your problem on CodeSandbox, using your code.我尝试使用您的代码在 CodeSandbox 上重现您的问题。 But everything works fine.但一切正常。

You can check it here:你可以在这里查看:

编辑居高临下的kapitsa-l2j4i

Possible problems:可能出现的问题:

  • May be you call addFlag somewhere right after calling dismissAllFlags .可能是您在调用addFlag之后立即在某处调用dismissAllFlags I added commented line in the sandbox example.我在沙盒示例中添加了注释行。 With this line the code works like you have described in the question.使用这一行,代码就像您在问题中描述的那样工作。
  • Another versions of dependencies.另一个版本的依赖项。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM