【问题标题】:connected react-redux component does not rerender upon state连接的 react-redux 组件不会根据状态重新渲染
【发布时间】:2017-11-20 10:21:42
【问题描述】:

我正在尝试编写一个简单的测试 react-redux 应用程序来开始使用 gmail API,现在状态更改不会强制重新渲染。

我做了什么:

我读过this question。那个人的问题似乎不是由connect-ing 组件引起的,但据我所知,我正在这样做。我读了this question。那个人的问题是由于误解了combineReducers 如何在状态上设置对象,但我不认为我有同样的误解。我确定我还有其他的!

这是我的顶级容器,带有路由:

'use strict'
import React from 'react'
import {Router, Route, IndexRedirect, browserHistory} from 'react-router'
import {render} from 'react-dom'
import {connect, Provider} from 'react-redux'

import store from './store'
import Home from './components/Home'
import Login from './components/Login'
import WhoAmI from './components/WhoAmI'
import NotFound from './components/NotFound'
import { handleClientLoad } from './gmailApi'

const ExampleApp = connect(
  ({ auth }) => ({ user: auth })
)(
  ({ user, children }) =>
    <div>
      <nav>
        {user ? <WhoAmI/> : <Login/>}
      </nav>
      {children}
    </div>
)

const onAppEnter = () => {
  handleClientLoad()
}

render(
  <Provider store={store}>
    <Router history={browserHistory}>
      <Route path="/" component={ExampleApp}>
        <IndexRedirect to="/home" />
        <Route path="/home" component={Home} onEnter={onAppEnter} />
      </Route>
      <Route path='*' component={NotFound} />
    </Router>
  </Provider>,
  document.getElementById('main')
)

这是我渲染组件的代码:

import React, { Component } from 'react'
import { handleAuthClick, handleSignOutClick } from '../gmailApi'
import store from '../store'
import { connect } from 'react-redux'


var authorizeButton = <button id="authorize-button" onClick={() => { handleAuthClick() }}>Authorize</button>
var signOutButton = <button id="signout-button" onClick={handleSignOutClick}>Sign Out</button>


const mapStateToProps = (state) => {
  return {
    labels: state.gmail.labels,
    isSignedIn: state.gmail.isSignedIn
  }
}

export const Labels = ({ isSignedIn, labels }) => {
  var button = isSignedIn ? signOutButton : authorizeButton
  console.log("labels ", labels)
  return (
    <div>
      {button}
      <ul>
        {labels && labels.map(label => <li>{label}</li>)}
      </ul>
    </div>
  )
}

export default class Home extends Component {

  constructor(props) {
    super(props)

    this.state = {labels: [], isSignedIn: false}
  }

  render() {
    return (
        <Labels labels={this.props.labels} isSignedIn={this.props.isSignedIn}/>
    )
  }

}

connect(mapStateToProps)(Home)

这是我的 gmail reducer 和 action creators:

let initialState = {
    labels: [],
    isSignedIn: false
}

const reducer = (state=initialState, action) => {
    const newState = Object.assign({}, state);
  switch (action.type) {
  case SIGNED_IN:
    newState.isSignedIn = action.isSignedIn
    return newState
  case RECEIVE_LABELS:
    newState.labels = action.labels
    return newState
  }

  return state
}

const SIGNED_IN = 'SIGNED_IN'
export const signedIn = isSignedIn => ({
  type: SIGNED_IN, isSignedIn
})


const RECEIVE_LABELS = 'LABELS'
export const receiveLabels = labels => ({
    type: RECEIVE_LABELS, labels
})


export default reducer

这里是我结合减速器的地方:

import { combineReducers } from 'redux'

const rootReducer = combineReducers({
  auth: require('./auth').default, 
  gmail: require('./gmail').default
})
export default rootReducer

这是显示我的控制台的屏幕截图。 (我不太了解这个跨框架起源错误,欢迎解释,但我认为这是我的问题的辅助,因为我克服了它并成功通过减速器传递了一个动作。)

我在第一次渲染时成功地控制台记录标签,但是在状态上设置标签后没有重新渲染,并且我不再控制台日志标签(或在页面上渲染标签列表)。

感谢您提供的任何帮助!

附言而且,为了完整起见,这里是我做异步 gmail api 的地方。 (我知道我现在没有遵循异步操作创建者的格式。我正在使用来自 gmail api 的示例代码,我只是试图首先启动并运行,然后扩展和清理我的代码。我不'认为问题可能出在此处,因为状态填充得很好;我只是无法重新渲染页面。)

import store from '../store'
import { signedIn, receiveLabels } from '../reducers/gmail'
import gapi from './gapi'
import '../store'

var CLIENT_ID = '<stack overflow doesn't need to know this>';

// Array of API discovery doc URLs for APIs used by the quickstart
var DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/gmail/v1/rest"];

// Authorization scopes required by the API; multiple scopes can be
// included, separated by spaces.
var SCOPES = "https://www.googleapis.com/auth/gmail.modify";


/**
 *  On load, called to load the auth2 library and API client library.
 */


export const handleClientLoad = function() {
    gapi.load('client:auth2', initClient);
}

/**
 *  Initializes the API client library and sets up sign-in state
 *  listeners.
 */
function initClient() {
    gapi.client.init({
        discoveryDocs: DISCOVERY_DOCS,
        clientId: CLIENT_ID,
        scope: SCOPES
    }).then(function () {
        // Listen for sign-in state changes.
        gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus)

        // Handle the initial sign-in state.
        updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());


    });
}

function updateSigninStatus(isSignedIn) {

        console.log(isSignedIn)
        store.dispatch(signedIn(isSignedIn))
}

/**
 *  Sign in the user upon button click.
 */
export const handleAuthClick = function(event) {
    console.log("got here")
    gapi.auth2.getAuthInstance().signIn();
    fetchLabels()
}

/**
 *  Sign out the user upon button click.
 */
export const handleSignoutClick = function(event) {
    gapi.auth2.getAuthInstance().signOut();
}

/**
 * Append a pre element to the body containing the given message
 * as its text node. Used to display the results of the API call.
 *
 * @param {string} message Text to be placed in pre element.
 */

/**
 * Print all Labels in the authorized user's inbox. If no labels
 * are found an appropriate message is printed.
 */
function fetchLabels() {
    console.log("gapi client ", gapi.client)
    gapi.client.gmail.users.labels.list({
        'userId': 'me'
    }).then(function (response) {
        var labels = response.result.labels;
        store.dispatch(receiveLabels(labels))
    })
}

【问题讨论】:

    标签: reactjs redux react-redux


    【解决方案1】:

    我看到的一个错误是您没有导出连接的组件。您导出组件,然后再连接它。 connect 函数返回一个新的高阶组件;它不会影响组件本身。此外,connect 函数接受第二个参数:将动作发送到状态存储的动作创建者。如果您希望它们触发您的减速器,则需要导入这些操作。

    你有:

    export default class Home extends Component {
    
      constructor(props) {
        super(props)
    
        this.state = {labels: [], isSignedIn: false}
      }
    
      render() {
        return (
            <Labels labels={this.props.labels} isSignedIn={this.props.isSignedIn}/>
        )
      }
    
    }
    
    connect(mapStateToProps)(Home)
    

    只需将其更改为:

    import * as actions from './nameOfActionsFolder';
    
    class Home extends Component {
    
      constructor(props) {
        super(props)
    
        this.state = {labels: [], isSignedIn: false}
      }
    
      render() {
        return (
            <Labels labels={this.props.labels} isSignedIn={this.props.isSignedIn}/>
        )
      }
    
    }
    
    export default connect(mapStateToProps, actions)(Home);
    

    这样您将导出连接的组件。

    【讨论】:

    • 我相信你是对的,我还没有投票并接受你的答案的唯一原因是,现在由于某种原因,与 gmail api 的连接不起作用,我没有无法根据状态更新标签,因此我无法验证您的建议是否解决了任何问题。我正在尝试找出问题所在,以及是否适合更新我的问题或提出新问题。
    • 是的,我不是那个 api 是如何工作的。此外,如果您使用 redux,则不需要本地状态。也许解决问题的好方法是摆脱 redux,并尝试使其与 react 组件中的本地状态一起工作,然后将功能分解到 redux 中。
    • 我修好了。我只需要将 fetchLabels() 作为 .then 调用 .signIn() 即可。我认为之前没有引起麻烦的唯一原因是我的登录状态一直存在于浏览器中,因此总有一个用户可以找到 fetchLabels。我会接受你的回答。感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-22
    • 2020-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多