mapDispatchToProps 不工作。 this.props.getCategories 不是函数
mapDispatchToProps not working. this.props.getCategories is not a function
我正在尝试使用 react-redux 从后端获取类别列表
看起来一切都很好,除非调度函数调用。
我刚刚在 Traversy Media
的 link 中使用了来自 youtube 的教程
减速器
import { GET_CATEGORIES } from '../actions/types.js';
const initialState = {
categories: []
}
export default function(state = initialState, action){
switch(action.type){
case GET_CATEGORIES:
return {
...state,
categories: action.payload
};
default:
return state;
}
}
操作:
import axios from "axios";
import { GET_CATEGORIES } from "./types";
//GET CATEGORIES
export const getCategories = () => dispatch => {
axios.get('/api/categories/')
.then(res => {
dispatch({
type: GET_CATEGORIES,
payload: res.data
});
}).catch(err => console.log(err));
};
分量:
import React, { Component } from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { getCategories } from '../actions/categories';
export class Categories extends Component {
static propTypes = {
categories: PropTypes.array.isRequired
}
componentDidMount() {
this.props.getCategories(); //no error when comment this line
}
render() {
return(
<div>
<h1>
Categories Component
</h1>
</div>
)
}
}
const mapStateToProps = state => ({
categories: state.categories.categories
})
export default connect(mapStateToProps, { getCategories })(Categories);
如果我在 componentDidMount 中注释该行,然后页面在 redux 存储中加载一个空数组,但如果我尝试调用该操作来实际获取数据,我会收到以下错误:
Uncaught TypeError: this.props.getCategories is not a function
编辑:
我以这种方式在 App.js 中导入类别组件:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from '../store';
import {Categories} from './Categories';
class App extends Component{
render() {
return (
<Provider store={store}>
<Categories />
</Provider>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'));
我还发现我从 webpack 收到以下警告:
WARNING in ./prj/frontend/src/components/Categories.js 63:17-30
export 'default' (imported as 'getCategories') was not found in '../actions/categories' (possible exports: getCategories)
@ ./prj/frontend/src/components/App.js 27:0-42 45:42-52
@ ./prj/frontend/src/index.js 1:0-35
没有发现提供的代码有任何问题,我的猜测是您错误地导入了一个未连接的组件:
// WRONG, you import non-connected component
import {Categories} from 'Categories.js'
// Your default export is wrapped with connect
import Categories from 'Categories.js';
在我阅读了关于connect
again, I think that an async action is not possible with connect's object shorthand form的文档之后。原因是对象shorthand形式假定对象包含ActionCreator
s
Object Shorthand Form
mapDispatchToProps may be an object where each field is an action creator.
ActionCreator
s 必须 return 一个动作。他们return 被派往商店的动作。 connect
方法通过使用 bindActionCreators
方法实现了这一点。
bindActionCreators(actionCreators, dispatch)
Turns an object whose values are action creators, into an object with the same keys, but with every action creator wrapped into a dispatch call so they may be invoked directly.
由于您的 getCategories
是一个 异步函数 它没有 return 一个动作。它不能 return 一个动作,因为该动作需要来自 axios 调用的 Promise
的有效负载,并且因为它没有 return 一个动作,所以没有任何东西可以被调度。这就是对象 shorthand 形式不起作用的原因。
这是一个使用 mapDispatchToProps
函数的版本。
const { Provider, connect } = ReactRedux;
const { createStore } = Redux;
const { Component } = React;
const initialState = {
categories: []
}
const GET_CATEGORIES = 'getCategories';
function reducer(state, action){
switch(action.type){
case GET_CATEGORIES:
return {
...state,
categories: action.payload
};
default:
return state;
}
}
const getCategories = (dispatch) => () => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((response) => response.json())
.then((data) => {
dispatch({
type: GET_CATEGORIES,
payload: data
});
}
);
};
class Categories extends Component {
componentDidMount() {
this.props.getCategories(); //no error when comment this line
}
render() {
return(
<div>
<h1>
Categories Component
</h1>
<p>{JSON.stringify(this.props.categories)}</p>
</div>
)
}
}
const mapStateToProps = state => ({
categories: state.categories
})
const mapDispatchToProps = (dispatch) => {
return {
getCategories: getCategories(dispatch) };
};
const ConnectedCategories = connect(mapStateToProps, mapDispatchToProps)(Categories);
const store = createStore(reducer, initialState)
ReactDOM.render(<Provider store={store}><ConnectedCategories/></Provider>, document.getElementById("root"));
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.1.1/redux.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.4/react-redux.js"></script>
<div id="root"></div>
我正在尝试使用 react-redux 从后端获取类别列表 看起来一切都很好,除非调度函数调用。 我刚刚在 Traversy Media
的 link 中使用了来自 youtube 的教程减速器
import { GET_CATEGORIES } from '../actions/types.js';
const initialState = {
categories: []
}
export default function(state = initialState, action){
switch(action.type){
case GET_CATEGORIES:
return {
...state,
categories: action.payload
};
default:
return state;
}
}
操作:
import axios from "axios";
import { GET_CATEGORIES } from "./types";
//GET CATEGORIES
export const getCategories = () => dispatch => {
axios.get('/api/categories/')
.then(res => {
dispatch({
type: GET_CATEGORIES,
payload: res.data
});
}).catch(err => console.log(err));
};
分量:
import React, { Component } from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { getCategories } from '../actions/categories';
export class Categories extends Component {
static propTypes = {
categories: PropTypes.array.isRequired
}
componentDidMount() {
this.props.getCategories(); //no error when comment this line
}
render() {
return(
<div>
<h1>
Categories Component
</h1>
</div>
)
}
}
const mapStateToProps = state => ({
categories: state.categories.categories
})
export default connect(mapStateToProps, { getCategories })(Categories);
如果我在 componentDidMount 中注释该行,然后页面在 redux 存储中加载一个空数组,但如果我尝试调用该操作来实际获取数据,我会收到以下错误:
Uncaught TypeError: this.props.getCategories is not a function
编辑:
我以这种方式在 App.js 中导入类别组件:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from '../store';
import {Categories} from './Categories';
class App extends Component{
render() {
return (
<Provider store={store}>
<Categories />
</Provider>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'));
我还发现我从 webpack 收到以下警告:
WARNING in ./prj/frontend/src/components/Categories.js 63:17-30
export 'default' (imported as 'getCategories') was not found in '../actions/categories' (possible exports: getCategories)
@ ./prj/frontend/src/components/App.js 27:0-42 45:42-52
@ ./prj/frontend/src/index.js 1:0-35
没有发现提供的代码有任何问题,我的猜测是您错误地导入了一个未连接的组件:
// WRONG, you import non-connected component
import {Categories} from 'Categories.js'
// Your default export is wrapped with connect
import Categories from 'Categories.js';
在我阅读了关于connect
again, I think that an async action is not possible with connect's object shorthand form的文档之后。原因是对象shorthand形式假定对象包含ActionCreator
s
Object Shorthand Form
mapDispatchToProps may be an object where each field is an action creator.
ActionCreator
s 必须 return 一个动作。他们return 被派往商店的动作。 connect
方法通过使用 bindActionCreators
方法实现了这一点。
bindActionCreators(actionCreators, dispatch)
Turns an object whose values are action creators, into an object with the same keys, but with every action creator wrapped into a dispatch call so they may be invoked directly.
由于您的 getCategories
是一个 异步函数 它没有 return 一个动作。它不能 return 一个动作,因为该动作需要来自 axios 调用的 Promise
的有效负载,并且因为它没有 return 一个动作,所以没有任何东西可以被调度。这就是对象 shorthand 形式不起作用的原因。
这是一个使用 mapDispatchToProps
函数的版本。
const { Provider, connect } = ReactRedux;
const { createStore } = Redux;
const { Component } = React;
const initialState = {
categories: []
}
const GET_CATEGORIES = 'getCategories';
function reducer(state, action){
switch(action.type){
case GET_CATEGORIES:
return {
...state,
categories: action.payload
};
default:
return state;
}
}
const getCategories = (dispatch) => () => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((response) => response.json())
.then((data) => {
dispatch({
type: GET_CATEGORIES,
payload: data
});
}
);
};
class Categories extends Component {
componentDidMount() {
this.props.getCategories(); //no error when comment this line
}
render() {
return(
<div>
<h1>
Categories Component
</h1>
<p>{JSON.stringify(this.props.categories)}</p>
</div>
)
}
}
const mapStateToProps = state => ({
categories: state.categories
})
const mapDispatchToProps = (dispatch) => {
return {
getCategories: getCategories(dispatch) };
};
const ConnectedCategories = connect(mapStateToProps, mapDispatchToProps)(Categories);
const store = createStore(reducer, initialState)
ReactDOM.render(<Provider store={store}><ConnectedCategories/></Provider>, document.getElementById("root"));
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.1.1/redux.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.4/react-redux.js"></script>
<div id="root"></div>