【问题标题】:The data sent from action is not received by reducer从action发送的数据没有被reducer接收到
【发布时间】:2019-01-03 20:54:12
【问题描述】:

我在我的 react 应用程序中使用 redux。我正在尝试从减速器获取数据,但是当我尝试这样做时。我遇到了一些错误。

未捕获的错误:给定操作“RECEIVE_CATEGORY_NAME”,reducer “categoriesReducer”返回未定义。要忽略一个动作,你必须 显式返回之前的状态。如果你想让这个减速器保持 没有值,你可以返回 null 而不是 undefined。

编写的逻辑在influencersNameReducer 的情况下工作正常,但在categoriesReducer 中显示错误 home_reducer.js

import { RECEIVE_INFLUENCERS_NAME, RECEIVE_CATEGORY_NAME } from './home_actions';

export const influencersNameReducer = (state = [], { type, influencers }) => {
  console.log(influencers)
  return type === RECEIVE_INFLUENCERS_NAME ? influencers : state
}


export const categoriesReducer = (state = [], { type, category }) => {
  console.log(type, category)
  return type === RECEIVE_CATEGORY_NAME ? category : state
}

home_actions.js

export const RECEIVE_INFLUENCERS_NAME = 'RECEIVE_INFLUENCERS_NAME'
export const RECEIVE_CATEGORY_NAME = 'RECEIVE_CATEGORY_NAME';

const receiveInfluencersName = influencers => ({ type: RECEIVE_INFLUENCERS_NAME, influencers })

const receiveCategoryName = categories => ({ type: RECEIVE_CATEGORY_NAME, categories })

export const fetchInfluencers = _ => dispatch => {
  $.ajax({
    method: 'get',
    url: 'vip_api/influencers',
    data: { name: _ },
    success(influencers) {
      dispatch(receiveInfluencersName(influencers))
    },
    error({ responseJSON, statusText }) {
      dispatch(receiveServerErrors(responseJSON || [statusText]))
    }
  })
}

export const fetchCategories = _ => dispatch => {
  $.ajax({
    method: 'get',
    url: 'vip_api/categories',
    data: { name: _ },
    success(categories) {
      dispatch(receiveCategoryName(categories))
    },
    error({ responseJSON, statusText }) {
      dispatch(receiveServerErrors(responseJSON || [statusText]))
    }
  })
}

store.js

import {influencersNameReducer, categoriesReducer} from './Vvip/Home/home_reducer';
import { composeWithDevTools } from 'redux-devtools-extension';

const reducer = combineReducers({
categoriesReducer,
  influencersNameReducer,
})

const composeEnhancers = composeWithDevTools({
  // Specify name here, actionsBlacklist, actionsCreators and other options if needed
});
export default (state = {}) => (
  createStore(reducer, state, composeEnhancers(applyMiddleware(errorMiddleware, timeoutMiddleware, thunk)))
)

index.js

import React, { Component } from 'react'
import Select, { components } from 'react-select'
import DateRange from '../../shared/_date_range';
import moment from 'moment';
import {ethnicities, ageRanges, isoCountries} from '../../constants';
import { connect } from 'react-redux';
import {fetchInfluencers, fetchCategories} from './home_actions';

class InfluencersForm extends Component {

    constructor() {
        super();
        this.state = {
            demography: null,
            dates : {
                startDate: moment(),
                endDate: moment()
            },
            influencersName: [],
        }
    }

    handleInfluencerName = event => {
        this.props.dispatch(fetchInfluencers(event))
    }

    handleSelectedInfluencer = event => {
        console.log(event)
        this.setState({
            isMenuOpenInfluencer : false
        })
    }
    componentWillReceiveProps(newProps) {
        console.log(newProps);
        if (newProps.influencersNameReducer && newProps.influencersNameReducer.length) {
            this.setState({
                influencersName: newProps.influencersNameReducer.map((influencer, index) => {
                    return ({ value: influencer, label: influencer })
                }),
            })
        }
    }

    handleInfluencerType = event => {
        console.log(event)
    }

    handleInfluencerCountry = event => {
        console.log(event)
    }

    handleInfluencerSubscribers = event => {
        console.log(event)
    }

    handleInfluencerVideosCreated = event => {
        console.log(event)
    }

    handleInfluencerCategory = event => {
        console.log(event)
        this.props.dispatch(fetchCategories(event))
    }

    onDemographyChange = event => {
        console.log(event.currentTarget.value)
        this.setState({
            demography: event.currentTarget.value
        })
    }

    handleInfluencerAge = event => {
        console.log(event)
    }

    handleInfluencerGender = event => {
        console.log(event)
    }

    handleInfluencerEthnicity = event => {
        console.log(event)
    }

    updateDates = event => {
        console.log(event)
        this.setState({
            dates: event
        })
    }
    render() {
        const influencersType = [
            { value: 'a', label: 'Type A' },
            { value: 'b', label: 'Type B' },
            { value: 'c', label: 'Type C' }
        ]

        const influencersCategory = [
            { value: 'a', label: 'Type A' },
            { value: 'b', label: 'Type B' },
            { value: 'c', label: 'Type C' }
        ]

        const influencersAge = ageRanges.map(age => ({ value: age, label: age }))

        const influencersGender = [
            { value: 'male', label: 'Male' },
            { value: 'female', label: 'Female' }
        ]

        const influencersKeywords = [
            { value: 'youtuber', label: 'Youtuber' },
            { value: 'vlogger', label: 'Vlogger' }
        ]

        const influencersCountry = Object.keys(isoCountries).map(code => ({ value: code, label: isoCountries[code] }))

        const DropdownIndicator = (props) => {
            return components.DropdownIndicator && (
                <components.DropdownIndicator {...props}>
                    <i className="fa fa-search" aria-hidden="true" style={{ position: 'initial', color: 'black' }}></i>
                </components.DropdownIndicator>
            );
        };
        return (
            <div className='home-forms influencer-form'>
                <div className='display-flex'>
                    <Select
                        options={this.state.influencersName}
                        onChange={this.handleSelectedInfluencer}
                        closeMenuOnSelect = {true}
                        isSearchable={true}
                        components={{ DropdownIndicator }}
                        onInputChange = {this.handleInfluencerName}
                        placeholder={'Start Typing Influencers Name'}
                        classNamePrefix="vyrill"
                        className="influencers influencers-icon-name" />

                    <Select
                        options={influencersType}
                        onChange={this.handleInfluencerType}
                        placeholder='Type of Influencers'
                        classNamePrefix="vyrill"
                        className="influencers influencers-icon-type" />

                    <Select
                        options={influencersCountry}
                        onChange={this.handleInfluencerCountry}
                        isSearchable={true}
                        components={{ DropdownIndicator }}
                        placeholder='Start Typing Country'
                        classNamePrefix="vyrill"
                        className="influencers influencers-icon-country" />
                </div>
                <div className='display-flex' style={{ marginTop: 32 }}>
                    <Select
                        options={influencersType}
                        onChange={this.handleInfluencerSubscribers}
                        placeholder='Number of Subscribers'
                        classNamePrefix="vyrill"
                        className="influencers influencers-icon-type" />

                    <Select
                        options={influencersType}
                        onChange={this.handleInfluencerVideosCreated}
                        placeholder='Number of Videos Created'
                        classNamePrefix="vyrill"
                        className="influencers influencers-icon-videos-created" />

                    <Select
                        options={influencersCategory}
                        onChange={this.handleInfluencerCategory}
                        onInputChange = {this.handleInfluencerCategory}
                        isSearchable={true}
                        components={{ DropdownIndicator }}
                        placeholder='Start Typing Category'
                        classNamePrefix="vyrill"
                        className="influencers influencers-icon-country influencers-icon-category" />  {/* remove influencers-icon-country later */}
                </div>
                <div style={{ marginTop: 50 }}>
                    <div className="display-flex">
                        <div className="icon-subscribers" style={{ marginTop: 4 }}></div>
                        <div style={{ fontWeight: 700, marginTop: 4 }}>Demographics</div>
                        <div className="radio-container">
                            <label>
                                <div style={{ fontSize: 14, marginTop: 4 }}>By influencers</div>
                                <input
                                    type="radio"
                                    name="demographics"
                                    value="influencers"
                                    checked={this.state.demography === 'influencers'}
                                    onChange={this.onDemographyChange} />
                                <span className="custom-radio">
                                </span>
                            </label>
                        </div>
                        <div className="radio-container">
                            <label>
                                <div style={{ fontSize: 14, marginTop: 4 }}>By people in videos</div>
                                <input
                                    type="radio"
                                    name="demographics"
                                    value="people in videos"
                                    checked={this.state.demography === 'people in videos'}
                                    onChange={this.onDemographyChange} />
                                <span className="custom-radio"></span>
                            </label>
                        </div>
                    </div>
                </div>
                <div className="display-flex" style={{ marginTop: 40 }}>
                    <Select
                        options={influencersAge}
                        onChange={this.handleInfluencerAge}
                        placeholder='Age'
                        classNamePrefix="vyrill"
                        className="influencers" />
                    <Select
                        options={influencersGender}
                        onChange={this.handleInfluencerGender}
                        placeholder='Gender'
                        classNamePrefix="vyrill"
                        className="influencers" />
                    <Select
                        options={ethnicities}
                        onChange={this.handleInfluencerEthnicity}
                        placeholder='Ethnicity'
                        classNamePrefix="vyrill"
                        className="influencers" />
                </div>
                <div style={{marginTop: 50}}>
                    <div style={{display: 'inline'}}>Contains keywords (in transcript):</div>
                    <span className="icon-info"></span>
                    <Select
                        options={influencersKeywords}
                        onChange={this.handleInfluencerName}
                        isSearchable={true}
                        classNamePrefix="vyrill"
                        placeholder= {" "}
                        className="influencers influencers-keywords"
                        styles = {{marginTop: 10}}/>
                </div>
                <div style={{marginTop: 50}} className="date-picker">
                    <div>Posted content time range</div>
                    <DateRange dates={ this.state.dates } updateDates={ this.updateDates }/>
                    <div className="icon-arrow-right"></div>
                </div>
            </div>
        )
    }
}

const mapStateToProps = ({ influencersNameReducer, categoriesReducer }) => ({
    influencersNameReducer,
    categoriesReducer
  })

export default connect(mapStateToProps)(InfluencersForm)

【问题讨论】:

    标签: javascript reactjs redux


    【解决方案1】:

    你需要修改你的reducer为:

    export const influencersNameReducer = (state = [], { type, influencers }) => {
        switch(type) {
            case RECEIVE_INFLUENCERS_NAME:
                return influencers;
            default: 
                return state;
        }
    }
    export const categoriesReducer = (state = [], { type, category }) => {
      switch(type) {
            case RECEIVE_CATEGORY_NAME:
                return category;
            default: 
                return state;
        }
    }
    

    在每一个动作中,调度器都会去往每一个reducer。因为在您的代码中,influencersNameReducer 减速器没有为RECEIVE_CATEGORY_NAME 类型做任何事情,因此返回未定义。所以你得到了错误。使用 switch case 就是这样做的方法。

    【讨论】:

    • 我认为您在问题描述中遗漏了一些要点。返回类型 === RECEIVE_INFLUENCERS_NAME ?影响者:声明此声明的作用完全相同。并且代码对于这个影响器减速器运行良好我正面临类别减速器的问题。
    • 这是偶然的,这不是使用减速器的方式。查看减速器文档redux.js.org/basics/reducers。这里它指定你必须使用 switch case,因为在一个动作中所有的 reducer 都会被调用。当reducer的类型没有找到或匹配时,你想返回状态。
    • 我试过你建议的方法,也没有成功。
    • 如果你的 reducer 只需要一个动作类型,你就不必使用 switch case,那么type === TYPE ? //receive some new state : state 是完全有效的。我认为问题可能是 API 端点返回未定义。
    • 当reducer的类型没有找到或者不匹配的时候想要返回状态。这就是三元的第三部分所做的。 : state 表示否则,返回状态。
    猜你喜欢
    • 1970-01-01
    • 2022-01-13
    • 1970-01-01
    • 2023-03-06
    • 1970-01-01
    • 2013-03-03
    • 2021-03-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多