1

我在 redux 中使用中间件,但是当我调度一个动作时,但在浏览器中出现错误:

Uncaught TypeError: Cannot read property 'type' of undefined

代码在这里:

import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import createLogger from 'redux-logger'
import rootReducer from '../reducers/reducers'
import Immutable from 'immutable'
const loggerMiddleware = createLogger()
//const initialState=0
 function configureStore() {
  return createStore(
    rootReducer,
    {postsBySubreddit:{},selectedSubreddit:'reactjs'},
    applyMiddleware(thunkMiddleware, loggerMiddleware)
  )
}
export default configureStore

我的action

import fetch from 'isomorphic-fetch'
import {actionCreator} from '../utils/creator'
const REQUEST_POSTS = 'REQUEST_POSTS'
const RECEIVE_POSTS = 'RECEIVE_POSTS'
const SELECT_SUBREDDIT = 'SELECT_SUBREDDIT'
const INVALIDATE_SUBREDDIT = 'INVALIDATE_SUBREDDIT'

export function selectSubreddit(reddit) {
  return {
    type: SELECT_SUBREDDIT,
    reddit
  }
}

export function invalidateReddit(reddit) {
  return {
    type: INVALIDATE_REDDIT,
    reddit
  }
}

function requestPosts(reddit) {
    console.log('requestPosts')
  return {
    type: REQUEST_POSTS,
    reddit
  }
}

function receivePosts(reddit, json) {
  return {
    type: RECEIVE_POSTS,
    reddit: reddit,
    posts: json.data.children.map(child => child.data),
    receivedAt: Date.now()
  }
}

function fetchPosts(reddit) {
  return dispatch => {
    dispatch(requestPosts(reddit))
    return fetch(`https://www.reddit.com/r/${reddit}.json`)
      .then(response => response.json())
      .then(json => dispatch(receivePosts(reddit, json)))
  }
}

function shouldFetchPosts(state, reddit) {
    if(state.postsByReddit.hasOwnProperty(reddit)){
        const posts = state.postsByReddit[reddit]
    }else{
        posts=false
    }
  if (!posts) {
    return true
  }
  if (posts.isFetching) {
    return false
  }
  return posts.didInvalidate
}

export function fetchPostsIfNeeded(reddit) {
  return (dispatch, getState) => {
    if (shouldFetchPosts(getState(), reddit)) {
      return dispatch(fetchPosts(reddit))
    }
  }
}

我的components

import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { selectSubreddit, fetchPostsIfNeeded, invalidateSubreddit } from '../actions/action'
import Picker from '../components/Picker'
import Posts from '../components/Posts'

class AsyncApp extends Component {
  constructor(props) {
    super(props)
    this.handleChange = this.handleChange.bind(this)
    this.handleRefreshClick = this.handleRefreshClick.bind(this)
  }

  componentDidMount() {
    const { dispatch, selectedSubreddit } = this.props
    dispatch(fetchPostsIfNeeded(selectedSubreddit))
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.selectedSubreddit !== this.props.selectedSubreddit) {
      const { dispatch, selectedSubreddit } = nextProps
      dispatch(fetchPostsIfNeeded(selectedSubreddit))
    }
  }

  handleChange(nextSubreddit) {
    this.props.dispatch(selectSubreddit(nextSubreddit))
  }

  handleRefreshClick(e) {
    e.preventDefault()

    const { dispatch, selectedSubreddit } = this.props
    console.log(this.props);
    dispatch(invalidateSubreddit(selectedSubreddit))
    dispatch(fetchPostsIfNeeded(selectedSubreddit))
  }

  render() {
    const { selectedSubreddit, posts, isFetching, lastUpdated } = this.props
    console.log(this.props)
    return (
      <div>
        <Picker value={selectedSubreddit}
                onChange={this.handleChange}
                options={[ 'reactjs', 'frontend' ]} />
        <p>
          {lastUpdated &&
            <span>
              Last updated at {new Date(lastUpdated).toLocaleTimeString()}.
              {' '}
            </span>
          }
          {!isFetching &&
            <a href='#'
               onClick={this.handleRefreshClick}>
              Refresh
            </a>
          }
        </p>
        {isFetching && posts.length === 0 &&
          <h2>Loading...</h2>
        }
        {!isFetching && posts.length === 0 &&
          <h2>Empty.</h2>
        }
        {posts.length > 0 &&
          <div style={{ opacity: isFetching ? 0.5 : 1 }}>
            <Posts posts={posts} />
          </div>
        }
      </div>
    )
  }
}

AsyncApp.propTypes = {
  selectedSubreddit: PropTypes.string.isRequired,
  posts: PropTypes.array.isRequired,
  isFetching: PropTypes.bool.isRequired,
  lastUpdated: PropTypes.number,
  dispatch: PropTypes.func.isRequired
}

function mapStateToProps(state) {
  const { selectedSubreddit, postsBySubreddit } = state
  console.log('1:'+postsBySubreddit)
  const {
    isFetching,
    lastUpdated,
    items: posts
  } = postsBySubreddit[selectedSubreddit] || {
    isFetching: true,
    items: []
  }

  return {
    selectedSubreddit,
    posts,
    isFetching,
    lastUpdated
  }
}

export default connect(mapStateToProps)(AsyncApp)

reducer

    import { combineReducers } from 'redux'
    import {reducerCreator} from '../utils/creator'
    import Immutable from'immutable'
    import {SELECT_SUBREDDIT, INVALIDATE_SUBREDDIT ,REQUEST_POSTS, RECEIVE_POSTS} from '../actions/action'
    let initialState=Immutable.fromJS({isFetching: false, didInvalidate: false,items:[]})

    function selectedSubreddit(action) {
      switch (action.type) {
      case SELECT_SUBREDDIT:
        return action.subreddit
      default:
        return state
      }
    }
    function postsBySubreddit(action) {
        console.log(action)
      switch (action.type) {
        case INVALIDATE_SUBREDDIT:
        case RECEIVE_POSTS:
        case REQUEST_POSTS:
          return state.merge({
            [action.subreddit]: posts(state[action.subreddit], action)
          })
        default:
          return state
      }
    }
    function posts(state=initialState,action) {
      switch (action.type) {
        case INVALIDATE_SUBREDDIT:
          return state.merge({
            didInvalidate: true
          })
        case REQUEST_POSTS:
          return state.merge({
            isFetching: true,
            didInvalidate: false
          })
        case RECEIVE_POSTS:
          return state.merge({
            isFetching: false,
            didInvalidate: false,
            items: action.posts,
            lastUpdated: action.receivedAt
          })
        default:
          return state 
        }
    }

    const rootReducer = combineReducers({
      postsBySubreddit,
     selectedSubreddit
    })
export default rootReducer

当我调用时fetchPostsIfNeeded,它调用fetchPosts ,然后调用dispatch(requestPosts(reddit))但我的减速器无法处理此操作,我在控制台上的减速器中打印操作,它显示undefined

4

1 回答 1

1

您的减速器postsBySubredditselectedSubreddit收到错误的参数列表;

他们应该是:postsBySubreddit(state=initialState,action)selectedSubreddit(state=initialState,action)

于 2016-04-15T07:46:45.890 回答