简体   繁体   English

如何等待 Redux 操作从 React 组件更改状态

[英]How to wait for a Redux action to change state from a React component

I'm in a component which has a prop currentLineIndex passed by its parent container and coming from a Redux reducer.我在一个组件中,它有一个由其父容器传递并来自 Redux 减速器的道具currentLineIndex

In the same component's function I update currentLineIndex with an action creator and then I want to scroll to the new currentLineIndex .在同一个组件的函数中,我用动作创建者更新currentLineIndex ,然后我想滚动到新的currentLineIndex But it's not already updated, so I scroll to the same line.但它还没有更新,所以我滚动到同一行。

I've tried using async / await as you'll see but it's not working.我试过使用async / await如你所见,但它不起作用。

In my component:在我的组件中:

const { currentLineIndex, setCurrentLineIndex } = props; // passed by the parent container 

const handlePlaybackEnd = async () => {
  const nextIndex = currentLineIndex + 1;
  // await don't wait until global state / component props gets updated
  await setCurrentLineIndex(nextLineIndex);
  // so when I scroll on next line, I scroll to the same line.
  scrollToCurrentLine(); 
};

const scrollToCurrentLine = () => {
  const currentLineEl = document.getElementById(currentLineIndex);
  currentLineEl.scrollIntoView({ block: 'start', behaviour: 'smooth' });
};

in actions/index.js :actions/index.js

export function setCurrentLineIndex(index) {
  return { type: SET_CURRENT_LINE_INDEX, payload: index };
}

in my reducer:在我的减速器中:

case SET_CURRENT_LINE_INDEX:
  return {
    ...state,
    currentLineIndex: action.payload,
  };

Action and reducers are working good and my component state is successfully updated, but it's already too late. Action 和 reducer 运行良好,我的组件状态已成功更新,但为时已晚。

I really need to rely on Redux state, not just to pass the currentLineIndex to scrollToCurrentLine() , that would be too easy :)我真的需要依赖 Redux 状态,而不仅仅是将currentLineIndex传递给scrollToCurrentLine() ,那太容易了:)

What would be the best solution to wait until my component state has been updated ?等待组件状态更新的最佳解决方案是什么?

I finally solved this by making my component a class component so I could use componentDidUpdate我最终通过使我的组件成为类组件来解决这个问题,这样我就可以使用componentDidUpdate

componentDidUpdate(prevProps) {
  if (prevProps.currentLineIndex !== this.props.currentLineIndex) {
    this.scrollToCurrentLine(this.props.currentLineIndex);
  }
}

handlePlaybackEnd = () => this.props.setCurrentLineIndex(this.props.currentLineIndex + 1);

2020 UPDATE 2020 更新

Hooks made it a lot simpler, no need for a class component, just use an effect: Hooks 让它变得简单了很多,不需要类组件,只需使用一个效果:

const scrollToCurrentLine = useCallback(() => {
  const currentLineEl = document.getElementById(currentLineIndex);
  currentLineEl.scrollIntoView({ block: 'start', behaviour: 'smooth' });
}, [currentLineIndex]);

useEffect(scrollToCurrentLine, [scrollToCurrentLine]);

One solution can be to define setCurrentLineIndex such that it receives a callback.一种解决方案是定义setCurrentLineIndex以便它接收回调。

//assuming the parent container is a class
setCurrentLineIndex = (index, cb)=>{
    //write the logic here then execute the callback 
    cb()
}

// Then in your child component, you could do something like this 
    setCurrentLineIndex(nextIndex, scrollToCurrentLine)

I solved a similar problem by creating a little npm module.我通过创建一个小的 npm 模块解决了类似的问题。 It allows you to subscribe to and listen for redux actions and executes the provided callback function as soon as the state change is complete.它允许您订阅和侦听 redux 操作,并在状态更改完成后立即执行提供的回调函数。 Usage is as follows.用法如下。 In your componentWillMount or componentDidMount hook:在您的 componentWillMount 或 componentDidMount 钩子中:

 subscribeToWatcher(this,[
      {  
        action:"SOME_ACTION",
        callback:()=>{
          console.log("Callback Working");
        },
        onStateChange:true
      },
    ]);

Detailed documentation can be found at this link可以在此链接中找到详细的文档

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

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