Redux-thunk调度一个动作并等待重新渲染

时间:2018-04-18 05:43:30

标签: javascript reactjs redux redux-thunk

import React from "react";
import { render } from "react-dom";
import { createStore, applyMiddleware } from "redux";
import { Provider, connect } from "react-redux";
import thunk from "redux-thunk";

const disabled = (state = true, action) => {
  return action.type === "TOGGLE" ? !state : state;
};

class Button extends React.Component {
  componentDidUpdate(prevProps) {
    if (prevProps.disabled !== this.props.disabled && !this.props.disabled) {
      //  this.ref.focus();  // uncomment this to see the desired effect
    }
  }
  render() {
    const { props } = this;
    console.log("rendering", props.value);
    return (
      <div>
        <input
          type="checkbox"
          onClick={() => {
            props.toggle();
            this.ref.focus(); // doesn't work
          }}
        />
        <input
          disabled={props.disabled}
          ref={ref => {
            this.ref = ref;
          }}
        />
      </div>
    );
  }
}

const toggle = () => ({
  type: "TOGGLE"
});

const A = connect(state => ({ disabled: state }), { toggle })(Button);

const App = () => (
  <Provider store={createStore(disabled, applyMiddleware(thunk))}>
    <A />
  </Provider>
);

render(<App />, document.getElementById("root"));

Edit redux-thunk-promise

我想在选中复选框时关注input。 但是,只有在使用this.ref.focus()重新呈现组件后才能调用props.disabled === false,因为input的{​​{1}}道具无法集中。

如果我在disabled中执行逻辑,我就能实现我想要的。但这不是一个干净的解决方案,因为逻辑特定于componentDidUpdate处理程序而不是生命周期事件。

还有其他方法可以实现吗? (最好使用工作代码框示例)

4 个答案:

答案 0 :(得分:2)

我认为最好的办法是不依靠refs use state来管理焦点。

此解决方案使用输入上的autoFocus道具,并在复选框的状态发生变化时对其进行修改。

import React from "react";
import { render } from "react-dom";
import { createStore, applyMiddleware } from "redux";
import { Provider, connect } from "react-redux";
import thunk from "redux-thunk";

const disabled = (state = true, action) => {
  return action.type === "TOGGLE" ? !state : state;
};

class Button extends React.Component {
  state = {
    checked: false,
    focus: false
  };
  componentDidUpdate(prevProps, prevState) {
    if (prevState.checked !== this.state.checked) {
      this.props.toggle();
      this.setState({
        focus: this.state.checked
      });
    }
  }
  render() {
    const { props } = this;
    const { checked, focus } = this.state;
    console.log("rendering", props.value, checked);
    return (
      <div>
        <input
          type="checkbox"
          checked={checked}
          onClick={({ target }) => {
            this.setState({ checked: target.checked });
          }}
        />
        <input key={`input_${checked}`} autoFocus={focus} />
      </div>
    );
  }
}

const toggle = () => ({
  type: "TOGGLE"
});

const A = connect(state => ({ disabled: state }), { toggle })(Button);

const App = () => (
  <Provider store={createStore(disabled, applyMiddleware(thunk))}>
    <A />
  </Provider>
);

render(<App />, document.getElementById("root"));

Edit redux-thunk-promise

我不确定原因,但在先前禁用组件时更改autoFocus道具不会触发重新渲染的输入。所以我还在输入中添加了一个键来强制它。

答案 1 :(得分:2)

这是一个假设情况和REACT中的一个开放问题(同时不是)因为它与自动对焦的HTML规范(https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Attributes#autofocus)一致。焦点是装饰性的非常棘手的事情之一,因为它是共享的全球状态的一部分。如果2个不相关的组件声明它们应该集中在一个渲染过程中,谁是对的?因此,REACT为您提供了自己管理该状态的钩子,但它不会为您做到这一点(因此,就像您正在使用的那样工作的地方)。

但是如果REACT添加了专注于渲染的选项(可能只是autoFocusOnRender),那就好了,如果有多个事情需要立即聚焦,那么让文档警告人们行为。理想情况下,这不会发生,因为具有良好用户体验的应用程序将具有在不同输入上调用autoFocusOnRender的特定条件。

我建议你做的是最好的方法:)。希望我们在REACT中对此进行改进。

答案 2 :(得分:1)

由于数据流的原因,我认为在您执行state调用之前,您可以放心更新的Redux focus()数据:

  1. 发送异步操作toggleThunk,并等待其解决方案
  2. then调度同步操作以更新state(新state数据),并等待其解析(?)
  3. then focus()您的参考
  4. https://codesandbox.io/s/r57v8r39om

    Edit redux-thunk-promise

    请注意,在您的OP中,您的toggle()动作创建者不是thunk。此外,强制执行您的thunk返回Promise是一个很好的规则,这样您就可以按照您描述的方式控制数据流。

    import React from "react";
    import { render } from "react-dom";
    import { createStore, applyMiddleware } from "redux";
    import { Provider, connect } from "react-redux";
    import thunk from "redux-thunk";
    
    const disabled = (state = true, action) => {
      return action.type === "TOGGLE" ? !state : state;
    };
    
    class Button extends React.Component {
      textInput = React.createRef();
    
      handleClick = () => {
        const { toggleThunk } = this.props;
        toggleThunk().then(() => {
          this.textInput.current.focus();
        });
      };
    
      render() {
        const { disabled, value } = this.props;
        return (
          <div>
            <input type="checkbox" onClick={this.handleClick} />
            <input disabled={disabled} ref={this.textInput} />
          </div>
        );
      }
    }
    
    // Action
    const toggle = () => ({
      type: "TOGGLE"
    });
    
    // Thunk
    const toggleThunk = () => dispatch => {
      // Do your async call().then...
      return Promise.resolve().then(() => dispatch(toggle()));
    };
    
    const A = connect(state => ({ disabled: state }), { toggleThunk })(Button);
    
    const App = () => (
      <Provider store={createStore(disabled, applyMiddleware(thunk))}>
        <A />
      </Provider>
    );
    
    render(<App />, document.getElementById("root"));
    

答案 3 :(得分:0)

您可以使用prop和ref来管理它。引用将避免重新输入输入(即autoFocus工作):

import React, { Component } from "react";
import { render } from "react-dom";
import { createStore, applyMiddleware } from "redux";
import { Provider, connect } from "react-redux";
import thunk from "redux-thunk";

const disabled = (state = true, action) => {
  return action.type === "TOGGLE" ? !state : state;
};

class Button extends Component {

  componentDidUpdate(prevProps) {
    if (!this.props.disabled && prevProps.disabled) {
      this.ref.focus();
    }
  }

  render() {
    const { disabled } = this.props;
    return (
      <div>
        <input
          type="checkbox"
          checked={!disabled}
          onClick={() => {
            this.props.toggle();
          }}
        />
        <input
          disabled={disabled}
          ref={ref => {
            this.ref = ref;
          }}
        />
      </div>
    );
  }
}

const toggle = () => ({
  type: "TOGGLE"
});

const A = connect(state => ({ disabled: state }), { toggle })(Button);

const App = () => (
  <Provider store={createStore(disabled, applyMiddleware(thunk))}>
    <A />
  </Provider>
);

render(<App />, document.getElementById("root"));

Edit redux-thunk-promise

相关问题