为什么映射状态到道具不明确? [英] Why does mapping state to props give undefined?

查看:125
本文介绍了为什么映射状态到道具不明确?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的Redux设置有问题。我没有单个文件的帖子操作和reducers的问题,但一旦添加了一个 searchQueries 节,它只显示 searchQueries 道具的未定义的值。



我尽可能地复制它,并修改第二组动作/ reducers,但我仍然以 undefined 道具在 searchQueries 的情况下。我收到所有道具,包括帖子的默认值。以下是每个代码的代码:



/actions/posts.js:

 'axios'的导入axios 

导出功能postsHasErrored(bool){
return {
type:'POSTS_HAS_ERRORED',
hasErrored:bool
}
}

导出功能postsIsLoading(bool){
return {
type:'POSTS_IS_LOADING',
isLoading:bool
}
}

导出功能postsFetchDataSuccess(posts){
return {
type:'POSTS_FETCH_DATA_SUCCESS',
posts
}
}

导出功能totalPagesFetchDataSuccess(totalPages){
return {
type:'TOTAL_PAGES_FETCH_DATA_SUCCESS',
totalPages
}
}

导出函数postsFetchData(url){
return(dispatch)=> {
dispatch(postsIsLoading(true))

axios.get(url)
.then(res => {
if(res.status! 200)throw Error(res.statusText)
dispatch(postsIsLoading(false))
return res
})
.then(res => {
dispatch postsFetchDataSuccess(res.data))
dispatch(totalPagesFetchDataSuccess(res.headers ['x-wp-totalpages']))
})
.catch(()=> dispatch(postsHasErrored (true)))
}
}

/actions/searchQueries.js :

  const readLocation =(name)=> {
let parameter = getParameterByName(name);
if(name ==='categories'){
if(parameter){
parameter = parameter.split(',')
for(let i = 0; i < parameter.length; i ++)parameter [i] = parseInt(parameter [i],10)
}
else parameter = []
}

if (参数=== null){
if(name ==='search')parameter =''
if(name ==='page')parameter = 1
}

console.log(参数)

返回参数
}

导出函数setSearchString(searchString){
return {
类型:'SET_SEARCH_STRING',
searchString
}
}

导出函数setSearchCategories(searchCategories){
return {
type: 'SET_SEARCH_CATEGORIES',
searchCategories
}
}

导出函数setSearchPage(searchPage){
return {
type:'SET_SEARCH_PAGE'
searchPage
}
}

export f unction searchQueriesSetting(){
return(dispatch)=> {
dispatch(setSearchString(readLocation('search')))
dispatch(setSearchCategories(readLocation('categories')))
dispatch(setSearchPage(readLocation('page' b $ b}
}

/reducers/posts.js:

  export function postsHasErrored(state = false,action){
switch(action.type){
case'POSTS_HAS_ERRORED'
return action.hasErrored

默认值:
返回状态
}
}

导出函数postsIsLoading(state = false,动作){
switch(action.type){
case'POSTS_IS_LOADING':
return action.isLoading

默认值:
返回状态
}
}

导出功能帖子(state = [],action){
switch(action.type){
case'POSTS_FETCH_DATA_SUCCESS':
return action.posts

默认值:
返回状态
}
}

导出功能总共页数(状态= 1,动作){
switch(action.type){
case'TOTAL_PAGES_FETCH_DATA_SUCCESS':
return parseInt(action.totalPages,10)

默认值:
返回状态
}
}

/ reducers /searchQueries.js:

 导出功能searchString(state ='',action){
switch(action.type ){
case'SET_SEARCH_STRING':
return action.searchString

默认值:
返回状态
}
}

export function searchCategories(state = [],action){
switch(action.type){
case'SET_SEARCH_CATEGORIES':
return action.searchCategories

默认值:
返回状态
}
}

导出函数searchPage(state = 1,action){
switch(action.type){
case'SET_SEARCH_PAGE':
return action.searchPage

默认值:
return sta te
}
}

/reducers/index.js:


  import {combineReducers} from'redux'
import {posts,totalPages,postsHasErrored,postsIsLoading} from'./posts'
import {searchString,searchCategories,searchPage} from'./searchQueries'

export default combineReducers({
posts,
postsHasErrored,
postsIsLoading,
totalPages,

searchString,
searchCategories,
searchPage
})

/components/PostsList.js

  //依赖项
import React, {component} from'react'
从'axios'导入axios
import {connect}从'react-redux'
//组件
import PostsListItem from'./PostsListItem'
从'./PostsPages'导入的帖子
//动作
从'../actions/posts'导入{postsFetchData}
import {searchQueriesSetting} from'../action s / searchQueries'
// styles
import'../styles/css/postsList.css'
//共享模块
import {createSearchUrl} from'../sharedModules / sharedModules'

class PostsList extends Component {

componentWillReceiveProps(nextProps){
if(nextProps.searchPage!== this.props.searchPage)this.componentDidMount ()
}

componentDidMount(){
this.props.searchQueriesSetting()
this.props.fetchData(createSearchUrl(
'http:/ / localhost / wordpress-api / wp-json / wp / v2 / posts?per_page = 1',
this.props.searchCategories,
this.props.searchString,
this.props。



render(){
console.log(this.props)
const {isLoading,hasErrored, post} = this.props
if ;

const postsList = posts.map(post =>< PostsListItem post = {post} key = {post.id} />)

return(
< div className ='posts-list'>
{postsList}
< PostsPages />
< / div>

}
}

const mapStateToProps =(state)=> {
return {
posts:state.posts,
hasErrored:state.postsHasErrored,
isLoading:state.postsIsLoading,
searchCategories:state.searchCategories,
searchString:state.searchString,
searchPage:state.searchPage
}
}
const mapDispatchToProps =(dispatch)=> {
return {
fetchData:(url)=> dispatch(postsFetchData(url)),
searchQueriesSetting:()=> dispatch(searchQueriesSetting())
}
}

导出默认连接(mapStateToProps,mapDispatchToProps)(PostsList)

/components/PostsPages.js

  //依赖项
导入反应,{component} from'react'
import {link} from'react-router-dom'
import {connect} from'react-redux'
// actions
import {setSearchPage} from'../actions/searchQueries'
//共享模块
import {createSearchUrl} from'../sharedModules/sharedModules'

class PostsPages extends Component {
isLinkEdgy =(pageNumber)=> {
if(parseInt(pageNumber,10)< = 1)return''
if(parseInt(pageNumber,10)> = parseInt(this.props.totalPages,10))返回此。 props.totalPages
return pageNumber
}

render(){
const {totalPages,currentPage,searchCategories,searchString,setSearchPage} = this.props

const previousUrl = createSearchUrl('/ blog',searchCategories,searchString,this.isLinkEdgy(parseInt(currentPage,10) - 1))
const nextUrl = createSearchUrl('/ blog',searchCategories,searchString, this.isLinkEdgy(parseInt(currentPage,10)+ 1))

return(
< div className ='posts-pages'>
< ul className =' post-pages-list'>
< li>< link to = {previousUrl} onClick = {()=> setSearchPage(this.isLinkEdgy(parseInt(currentPage,10) - 1))}> ;上一页< / Link>< / li>
< li>< link to = {nextUrl} onClick = {()=> setSearchPage(this.i sLinkEdgy(parseInt(currentPage,10)+ 1))}>下一页< / Link>< / li>
< / ul>
< / div>

}
}

const mapStateToProps =(state)=> {
return {
totalPages:state.totalPages,
currentPage:state.searchPage,
searchCategories:state.searchCategories,
searchString:state.searchString
}
}
const mapDispatchToProps =(dispatch)=> {
return {
setSearchPage:(searchPage)=> dispatch(setSearchPage(searchPage))
}
}

导出默认连接(mapStateToProps,mapDispatchToProps)(postsPages)


解决方案

这是因为您正在访问错误的状态。看看你的 combineReducers call:

  export default combineReducers({ 
帖子,
postsHasErrored,
postsIsLoading,
totalPages,

setSearchString,
setSearchCategories,
setSearchPage
} )

Per Redux文档


combineReducers(reducers )



状态对象的形状与传递的 reducers的键匹配


因此,您的状态这个:

  {
posts:...,
postsHasErrored:...,
postsIsLoading:...,
totalPages:...,
setSearchString:...,
setSearchCategories:...,
setSearchPage:...
}

在你身上

  currentPage:state.searchPage,
searchCategories:state.searchCategories,
searchString:state.searchString

由于 state.searchPage 而另外两个不存在于状态对象中,你得到 undefined 。相反,请确保您访问与reducers具有相同名称的键:

  currentPage:state.setSearchPage,
searchCategories:state.setSearchCategories,
searchString:state.setSearchString

或者只是重命名您的reducers (这是现在更好的,因为它们是误会)。删除reducer上的 set 前缀,它们不是动作


I'm having a problem with my setup of Redux. I didn't have a problem with single file of posts actions and reducers, but as soon as added a searchQueries sections, it shows only undefined values for the searchQueries props.

I've tried copying it as far as I can and modifying it for the second set of actions/reducers, but I'm still ending up with undefined props in the case of searchQueries. I'm getting all the props, including the default values in the case of posts. Here's the code for each of these:

/actions/posts.js:

import axios from 'axios'

export function postsHasErrored(bool) {
  return {
    type: 'POSTS_HAS_ERRORED',
    hasErrored: bool
  }
}

export function postsIsLoading(bool) {
  return {
    type: 'POSTS_IS_LOADING',
    isLoading: bool
  }
}

export function postsFetchDataSuccess(posts) {
  return {
    type: 'POSTS_FETCH_DATA_SUCCESS',
    posts
  }
}

export function totalPagesFetchDataSuccess(totalPages) {
  return {
    type: 'TOTAL_PAGES_FETCH_DATA_SUCCESS',
    totalPages
  }
}

export function postsFetchData(url) {
  return (dispatch) => {
    dispatch(postsIsLoading(true))

    axios.get(url)
      .then(res => {
        if (res.status !== 200) throw Error(res.statusText)
        dispatch(postsIsLoading(false))
        return res
      })
      .then(res => {
        dispatch(postsFetchDataSuccess(res.data))
        dispatch(totalPagesFetchDataSuccess(res.headers['x-wp-totalpages']))
      })
      .catch(() => dispatch(postsHasErrored(true)))
  }
}

/actions/searchQueries.js:

const readLocation = (name) => {
    let parameter = getParameterByName(name);
    if (name === 'categories') {
      if (parameter) {
        parameter = parameter.split(',')
        for (let i = 0; i < parameter.length; i++) parameter[i] = parseInt(parameter[i], 10)
      }
      else parameter = []
    }

    if (parameter === null) {
      if (name === 'search') parameter = ''
      if (name === 'page') parameter = 1
    }

    console.log(parameter)

    return parameter
  }

export function setSearchString(searchString) {
  return {
    type: 'SET_SEARCH_STRING',
    searchString
  }
}

export function setSearchCategories(searchCategories) {
  return {
    type: 'SET_SEARCH_CATEGORIES',
    searchCategories
  }
}

export function setSearchPage(searchPage) {
  return {
    type: 'SET_SEARCH_PAGE',
    searchPage
  }
}

export function searchQueriesSetting() {
  return (dispatch) => {
    dispatch(setSearchString(readLocation('search')))
    dispatch(setSearchCategories(readLocation('categories')))
    dispatch(setSearchPage(readLocation('page')))
  }
}

/reducers/posts.js:

export function postsHasErrored(state = false, action) {
  switch (action.type) {
    case 'POSTS_HAS_ERRORED':
      return action.hasErrored

    default:
      return state
  }
}

export function postsIsLoading(state = false, action) {
  switch (action.type) {
    case 'POSTS_IS_LOADING':
      return action.isLoading

    default:
      return state
  }
}

export function posts(state = [], action) {
  switch (action.type) {
    case 'POSTS_FETCH_DATA_SUCCESS':
      return action.posts

    default:
      return state
  }
}

export function totalPages(state = 1, action) {
  switch (action.type) {
    case 'TOTAL_PAGES_FETCH_DATA_SUCCESS':
      return parseInt(action.totalPages, 10)

    default:
      return state
  }
}

/reducers/searchQueries.js:

export function searchString(state = '', action) {
  switch (action.type) {
    case 'SET_SEARCH_STRING':
      return action.searchString

    default:
      return state
  }
}

export function searchCategories(state = [], action) {
  switch (action.type) {
    case 'SET_SEARCH_CATEGORIES':
      return action.searchCategories

    default:
      return state
  }
}

export function searchPage(state = 1, action) {
  switch (action.type) {
    case 'SET_SEARCH_PAGE':
      return action.searchPage

    default:
      return state
  }
}

/reducers/index.js:

import { combineReducers } from 'redux'
import { posts, totalPages, postsHasErrored, postsIsLoading } from './posts'
import { searchString, searchCategories, searchPage } from './searchQueries'

export default combineReducers({
  posts,
  postsHasErrored,
  postsIsLoading,
  totalPages,

  searchString,
  searchCategories,
  searchPage
})

/components/PostsList.js

// dependencies
import React, { Component } from 'react'
import axios from 'axios'
import { connect } from 'react-redux'
// components
import PostsListItem from './PostsListItem'
import PostsPages from './PostsPages'
// actions
import { postsFetchData } from '../actions/posts'
import { searchQueriesSetting } from '../actions/searchQueries'
// styles
import '../styles/css/postsList.css'
// shared modules
import { createSearchUrl } from '../sharedModules/sharedModules'

class PostsList extends Component {

  componentWillReceiveProps(nextProps) {
    if (nextProps.searchPage !== this.props.searchPage) this.componentDidMount()
  }

  componentDidMount() {
    this.props.searchQueriesSetting()
    this.props.fetchData(createSearchUrl(
      'http://localhost/wordpress-api/wp-json/wp/v2/posts?per_page=1',
      this.props.searchCategories,
      this.props.searchString,
      this.props.searchPage
    ))
  }


  render() {
    console.log(this.props)
    const { isLoading, hasErrored, posts } = this.props
    if (isLoading) return <div className='posts-list'><h2 className='loading'>Loading...</h2></div>

    const postsList = posts.map(post => <PostsListItem post={post} key={post.id} />)

    return (
      <div className='posts-list'>
        {postsList}
        <PostsPages />
      </div>
    )
  }
}

const mapStateToProps = (state) => {
  return {
    posts: state.posts,
    hasErrored: state.postsHasErrored,
    isLoading: state.postsIsLoading,
    searchCategories: state.searchCategories,
    searchString: state.searchString,
    searchPage: state.searchPage
  }
}
const mapDispatchToProps = (dispatch) => {
  return {
    fetchData: (url) => dispatch(postsFetchData(url)),
    searchQueriesSetting: () => dispatch(searchQueriesSetting())
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(PostsList)

/components/PostsPages.js

// dependencies
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import { connect } from 'react-redux'
// actions
import { setSearchPage } from '../actions/searchQueries'
// shared modules
import { createSearchUrl } from '../sharedModules/sharedModules'

class PostsPages extends Component {
  isLinkEdgy = (pageNumber) => {
    if (parseInt(pageNumber, 10) <= 1) return ''
    if (parseInt(pageNumber, 10) >= parseInt(this.props.totalPages, 10)) return this.props.totalPages
    return pageNumber
  }

  render() {
    const { totalPages, currentPage, searchCategories, searchString, setSearchPage } = this.props

    const previousUrl = createSearchUrl('/blog', searchCategories, searchString, this.isLinkEdgy(parseInt(currentPage, 10) - 1))
    const nextUrl = createSearchUrl('/blog', searchCategories, searchString, this.isLinkEdgy(parseInt(currentPage, 10) + 1))

    return (
      <div className='posts-pages'>
        <ul className='posts-pages-list'>
          <li><Link to={previousUrl} onClick={() => setSearchPage(this.isLinkEdgy(parseInt(currentPage, 10) - 1))}>Prev page</Link></li>
          <li><Link to={nextUrl} onClick={() => setSearchPage(this.isLinkEdgy(parseInt(currentPage, 10) + 1))}>Next page</Link></li>
        </ul>
      </div>
    )
  }
}

const mapStateToProps = (state) => {
  return {
    totalPages: state.totalPages,
    currentPage: state.searchPage,
    searchCategories: state.searchCategories,
    searchString: state.searchString
  }
}
const mapDispatchToProps = (dispatch) => {
  return {
    setSearchPage: (searchPage) => dispatch(setSearchPage(searchPage))
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(PostsPages)

解决方案

That's because you're accessing the wrong par of state. Take a look at your combineReducers call:

export default combineReducers({
  posts,
  postsHasErrored,
  postsIsLoading,
  totalPages,

  setSearchString,
  setSearchCategories,
  setSearchPage
})

Per the Redux documentation:

combineReducers(reducers)

The shape of the state object matches the keys of the passed reducers.

Thus your state object actually looks like this:

{
  posts: ...,
  postsHasErrored: ...,
  postsIsLoading: ...,
  totalPages: ...,
  setSearchString: ...,
  setSearchCategories: ...,
  setSearchPage: ...
}

In your mapDispatchToProps, you're trying to access the wrong part of state:

currentPage: state.searchPage,
searchCategories: state.searchCategories,
searchString: state.searchString

Since state.searchPage and the other two don't exist in the state object, you get undefined. Instead, make sure you access the keys which have the same name as the reducers:

currentPage: state.setSearchPage,
searchCategories: state.setSearchCategories,
searchString: state.setSearchString

Or just rename your reducers (which would be preferable as they are misnomers right now). Get rid of the set prefix on the reducers, they are not actions.

这篇关于为什么映射状态到道具不明确?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆