为什么我的映射道具值在发送

时间:2016-11-12 02:35:34

标签: reactjs redux react-redux

我正在试图找出为什么在调用dispatch后没有更新道具(从redux商店映射)。例如,在下面的代码中发送RANDO动作,该动作设置一个随机数。如果我访问this.props.rando,则在发送后立即不包含更新的值。这是预期的行为,还是我做错了什么?

// reducer.js

module.exports = (state = {}, action) => {
  const newRando = Math.round(Math.random() * 100)
  console.log("Reducer: rando = ", newRando);
  return { rando: newRando }
}

// app.jsx
const { connect } = require('react-redux')
const React = require('react')

class App extends React.Component {
  onClick() {
    console.log("Clicked: rando = ", this.props.rando)
    this.props.dispatch({ type: 'RANDO' })
    console.log("After Dispatch: rando = ", this.props.rando)
    setTimeout(() => console.log("After setTimeout: rando = ", this.props.rando), 1)
  }

  render() {
    return (
      <div>
        <div onClick={() => this.onClick()}>HI</div>
      </div>)
  }
}

App.propTypes = {
  dispatch: React.PropTypes.func.isRequired,
  rando: React.PropTypes.number.isRequired,
}

const mapStateToProps = state => {
  return ({
    rando: state.rando
  })
}

module.exports = connect(mapStateToProps)(App)

点击HI div后的输出是

Clicked: rando = 36 Reducer: rando = 48 After Dispatch: rando = 36 After setTimeout: rando = 48

3 个答案:

答案 0 :(得分:2)

this.props.rando仅在App收到新道具后才更改其值。您可以在componentWillReceiveProps

中获取新值
componentWillReceiveProps(nextProps){
  console.log(this.props.rando);   //orignal value
  console.log(nextProps.rando);    //changed value
}

render() {
  console.log(this.props.rando);    //changed value. Render is invoked after componentWillReceiveProps. At this time, props has set to the new value.
  return (
    <div>
      <div onClick={() => this.onClick()}>HI</div>
    </div>)
}

答案 1 :(得分:1)

如果您在分派操作后(重新渲染之前)确实需要新的“rando”值,则可能需要查看使用thunks

在决定你是否真的需要thunks时首先阅读:

How to dispatch a Redux action with a timeout?

const thunk = ReduxThunk.default;
const { createStore, applyMiddleware, bindActionCreators } = Redux;
const { connect, Provider } =  ReactRedux;

const actions = {
  nextRandom() {
    return (dispatch, getState) => {
      dispatch({
        type: 'RANDO',
      });
      return getState().rando;
    };
  }
};

const reducer = (state = {rando: Math.round(Math.random() * 100)}, action) => {
  switch (action.type) {
    case 'RANDO': {
      const newRando = Math.round(Math.random() * 100);
      console.log("Reducer: rando =", newRando);
      return {...state, rando: newRando };
    }
    default:
      return state;
  }
};

const store = createStore(reducer, applyMiddleware(thunk));

const render = () => {
  ReactDOM.render(<Root />, document.getElementById("root"));
};

store.subscribe(render);

class App extends React.Component {
  onClick() {
    console.log("Clicked: rando =", this.props.rando)
    const returned = this.props.nextRandom();
    console.log('After Dispatch returned: rando =', returned);
    console.log("After Dispatch props: rando =", this.props.rando)
    setTimeout(() => console.log("After setTimeout: rando =", this.props.rando), 1)
  }

  render() {
    return (
      <div>
        <div onClick={() => this.onClick()}>HI</div>
        <div>{this.props.rando}</div>
      </div>
    );
  }
}

App.propTypes = {
  rando: React.PropTypes.number.isRequired,
  nextRandom: React.PropTypes.func.isRequired,
}

const mapStateToProps = state => ({
  rando: state.rando,
});

const mapDispatchToProps = dispatch => ({
  nextRandom: bindActionCreators(actions.nextRandom, dispatch),
});

const ConnectedApp = connect(mapStateToProps, mapDispatchToProps)(App);

const Root = () => (
  <Provider store={store}>
    <ConnectedApp />
  </Provider>
);

render();
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.6.0/redux.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/4.4.5/react-redux.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux-thunk/2.1.0/redux-thunk.js"></script>
<div id="root"></div>

答案 2 :(得分:1)

您不应使用props来访问状态。将状态在不同时间视为电影中的帧。他们没有依附,也无法相互沟通。

您访问的道具应该被认为是严格不可变的。将您的React jsx想象成一个从头开始渲染整个应用程序的函数。如果有任何变化,您的整个应用程序将替换为新版本。

实际上这不是发生的事情,但这是因为没有Redux,你需要一个存储状态的地方,并且因为实际渲染整个应用程序不会像使用优化的渲染路径那样高效。但你仍然应该把它想象成整个应用程序从头开始重新呈现,因为这是使用React的主要好处。

你的渲染函数严格地在一个帧/状态内运行,如果它试图使用setState,那么React会抱怨。如果它尝试dispatch一个动作,Redux或React会抱怨。您不应该尝试更改已经渲染的帧。

onClick代码在帧渲染之间运行。这意味着它总能看到旧框架。新帧尚未就绪,直到下一次渲染才会准备好。因此,你看不到它。 Redux和React将紧密合作,很快就会启动一个新的渲染周期。

正如其他答案所示,您可以使用Redux-Thunk将onClick的代码移动到thunked action creator,您可以在其中访问之前和之后的状态。

但相反,请考虑取消需要之前和之后的状态。您可以dispatch只需要在reducer内部修改状态(当然是不可变的)。减速机可以完全进入该州。

然后将它留给Redux,React-Redux和React在屏幕上“实现它”。那里有一些魔法,它不是同步的。但你不必担心它。