I'm using React-Redux with typescript and am trying to access a function defined in mapDispatchToProps, however I am getting the following error:
Uncaught TypeError: this.props.getStoreList is not a function
at t.componentDidMount
The two files involved are container.tsx and mapper.ts. I've tried putting the contents of mapper.ts into container.tsx in case it was an issue with importing, however that doesn't fix the error.
The scenario is very similar to this previous stackoverflow question: mapDispatchToProps is not putting my function into props however the solution doesn't seem to apply to my situation.
container.tsx
import * as React from 'react';
import { connect } from "react-redux";
import { mapStateToProps, mapDispatchToProps } from "./mapper";
import { IStoreListComponentProps } from './component';
export interface IStoreListContainerProps extends IStoreListComponentProps {
fetchStoreList?: () => void;
}
export class StoreListContainer extends React.Component<IStoreListContainerProps> {
componentDidMount() {
this.props.fetchStoreList();
}
render() {
return <div>Example</div>;
}
}
export default connect(mapStateToProps, mapDispatchToProps)(StoreListContainer);
mapper.ts
import { fetchStoreList } from "./actions/fetch-store-list";
import { IState } from "../../features/store/model";
export const mapDispatchToProps = (dispatch: any) => {
return {
getStoreList: () => { dispatch(fetchStoreList()); },
};
};
export const mapStateToProps = (state: IState) => {
return {
storeList: state.storeList
};
};
Thank you for helping!
export const mapDispatchToProps = (dispatch: any) => {
return {
getStoreList: () => dispatch(fetchStoreList()),
};
};
try writing it this way
Related
I'm trying to learn redux-observables but I seem to be having an issue getting my app to return data. I keep getting the error below and I'm not sure where I'm going wrong or what the error actually means.
I'm trying to learn redux-observables but I seem to be having an issue getting my app to return data. I keep getting the error below and I'm not sure where I'm going wrong or what the error actually means.
I'm trying to learn redux-observables but I seem to be having an issue getting my app to return data. I keep getting the error below and I'm not sure where I'm going wrong or what the error actually means.
I'm trying to learn redux-observables but I seem to be having an issue getting my app to return data. I keep getting the error below and I'm not sure where I'm going wrong or what the error actually means.
ERR: fetchData is not a function
I need help
Contsants
export const FETCH_DATA = "FETCH_DATA";
export const FETCH_DATA_FAIL = "FETCH_DATA_FAIL ";
Action
import { FETCH_DATA, FETCH_DATA_FAIL } from "../contsants/contsants";
export const fetchData = (exampleData = {}) => ({
type: FETCH_DATA,
payload: exampleData
});
export const fetchDataFail = () => ({
type: FETCH_DATA_FAIL
});
Store
import { createStore } from "redux";
import rootReducer from "../Reducer/reducer";
const store = createStore(rootReducer);
export default store;
Reducer
import { FETCH_DATA, FETCH_DATA_FAIL } from "../contsants/contsants";
import { combineReducers } from "redux";
const initialState = {};
export const exampleData = (state = initialState, action: any) => {
switch (action.type) {
case FETCH_DATA:
return action.payload;
case FETCH_DATA_FAIL:
return {};
default:
return state;
}
};
export default combineReducers({
exampleData
});
Epics
import "rxjs";
import axios from "axios";
import { from, of } from "rxjs";
import { mergeMap, map, catchError } from "rxjs/operators";
import { ofType } from "redux-observable";
import { FETCH_DATA } from "../contsants/contsants";
import { fetchData, fetchDataFail } from "../Actions/action"
export const exampleEpic = (action$: any) =>
action$.pipe(
ofType(FETCH_DATA),
mergeMap((action) =>
from(axios.get("jsonplaceholder.typicode.com/todos/1")).pipe(
map((response) => fetchData(response.data)),
catchError(() => of(fetchDataFail()))
)
)
);
App
import { fetchData } from './Actions/action'
import { connect } from "react-redux";
function App(data: any, fetchData: any) {
const handleClickShowsTodos = () => {
fetchData()
console.log(data);
}
return (
<div>
<input type="text" />
<button onClick={handleClickShowsTodos}>ShowsTodo</button>
</div>
);
}
const mapStateToProps = (state: any) => {
return {
data: state
};
};
function mapDispatchToProps(dispatch: any) {
return {
fetchData: () => {
console.log('dispatch')
dispatch(fetchData())
}
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
Take a look at the following slice I've created in TypeScript (storeSlice.ts):
import { createSlice, PayloadAction } from "#reduxjs/toolkit";
import axios from "axios";
import { AppThunk } from "../../app/store";
import { Summoner } from "../../models/Summoner";
export interface StoreState {
summoners: Summoner[]
}
const initialState: StoreState = {
summoners: []
}
export const storeSlice = createSlice({
name: 'store',
initialState,
reducers: {
getSummonersSuccess: (state: StoreState, action: PayloadAction<Summoner[]>) => {
state.summoners = action.payload;
}
}
});
export const { getSummonersSuccess } = storeSlice.actions;
export const getSummoners = (): AppThunk => (dispatch) => {
axios.get("api/summoners").then((response) => {
dispatch(getSummonersSuccess(response.data));
});
}
export default storeSlice.reducer;
It's pretty basic. It has an initial state that is of type StoreState which contains an array of type Summoner.
Now, the component that uses this slice looks like this (Store.tsx):
import React, { Dispatch } from 'react';
import { connect } from 'react-redux';
import { getSummoners, StoreState } from './storeSlice';
interface PropState {
store: StoreState
}
const Store = (props: StoreState) => {
console.log("store props", props); //This contains my "getSummoners" action, but I can't access it because it's not defined in "StoreState"
return (
<h1>Hello!</h1>
);
}
const mapStateToProps = (state: PropState) => {
console.log("Store state", state);
return { summoners: state.store.summoners };
};
const mapDispatchToProps = (dispatch: Dispatch<any>) => {
return {
getSummoners: dispatch(getSummoners)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Store);
When I log the props in the component, I get the state that I've mapped with mapStateToProps, and I also get the getSummoners action that I've mapped with mapDispatchToProps.
However, I cannot actually access the getSummoners action because it is not defined in StoreState.
I do not want to have to define the actions in every default state type that I create.
Is there something I can do to be able to use the dispatch actions in my code whilst keeping TypeScript happy?
Apologies if this question doesn't make too much sense, I'm fairly new to this tech stack.
In your mapDispatchToProps you are inadvertanly calling the dispatch rather than returning a function to call the dispatch from your component. Change it to:
const mapDispatchToProps = (dispatch: Dispatch<any>) => {
return {
getSummoners: () => dispatch(getSummoners)
}
}
This should clear up your problems. If it doesn't, please update your post with the exact error that you are getting.
As a sidenote, this connect higher-order component with mapStateToProps and mapDispatchToProps has been around for a while and it used to be the only way to get data from the state into your component. It still works, but nowadays it is recommended that you use the newer hooks useSelector and useDispatch instead.
Post component:
import React from 'react';
import './post.styles.scss';
import { connect } from 'react-redux';
import { requestContents } from '../../redux/post/post.actions';
class Post extends React.Component {
componentWillMount(){
}
render(){
return (
<div>
</div>
)
}
}
const mapDispatchToProps = (dispatch) => {
return {
onRequestContents : dispatch(requestContents())
}
}
const mapStateToProps = (state) => {
return {
posts: state.post.posts,
isPending: state.post.isPending
}
}
User component:
export default connect(mapStateToProps, mapDispatchToProps)(Post)
import React from 'react';
import './user.styles.scss';
import { connect } from 'react-redux';
import { requestUsers } from '../../redux/user/user.actions';
class User extends React.Component {
componentWillMount(){
this.props.onRequestUsers();
}
render(){
return (
<div>
</div>
}
}
const mapDispatchToProps = (dispatch) => {
return {
onRequestUsers: () => dispatch(requestUsers())
}
}
const mapStateToProps = (state) => {
return {
users: state.user.users,
isPending: state.user.isPending
}
}
export default connect(mapStateToProps, mapDispatchToProps)(User)
Here I am using redux with React.
In user component i am calling onRequestUsers inside componentWillMount()
but in post component i am not calling onRequestContents inside componentWillMount()
But still how it is calling and display in my redux-logger
I am calling mapDispatchToProps only inside user component
Please have a look
You're calling requestContents in your mDTP call:
const mapDispatchToProps = (dispatch) => {
return {
onRequestContents: dispatch(requestContents())
}
}
That's what those two parens do:
requestContents()
Omit them:
onRequestContents: dispatch(requestContents)
That said: there's something odd with your function. Normally you'd mDTP with a function, e.g.,
onRequestContents: () => dispatch(requestContents())
(Used when you need to pass parameters, like an event.)
I'm trying to connect redux to a component using Typescript and keep running into the same error.
Argument of type 'typeof BaseLayoutUnconnected' is not assignable to
parameter of type 'Component < any, {}, any>'. Property 'setState' is
missing in type 'typeof BaseLayoutUnconnected'.
import * as React from 'react';
import { IBaseLayoutProps, IBaseLayoutState } from './base-layout.types';
import { ChatContainer } from '../../components';
import { connect, DispatchProp } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux';
import { RouteComponentProps } from 'react-router';
import { ChatActions } from 'app/actions';
import { RootState } from 'app/reducers';
import { omit } from 'app/utils';
export const mapStateToProps = (state: RootState, ownProps) => {
return {
chatItems: state.chatItems
};
};
export const mapDispatchToProps = (dispatch: Dispatch) => ({
actions: bindActionCreators(omit(ChatActions, 'Type'), dispatch)
});
export class BaseLayoutUnconnected extends React.Component<IBaseLayoutProps, IBaseLayoutState> {
constructor(props) {
super(props);
this.state = {};
}
render() {
const { actions, chatItems } = this.props;
return <ChatContainer actions={actions} chatItems={chatItems} />;
}
}
export const BaseLayout = connect(
mapStateToProps,
mapDispatchToProps
)(BaseLayoutUnconnected);
This is being called in my app.tsx via
<Route exact={true} path="/" component={BaseLayout} />
Here are the props and state
export interface IBaseLayoutProps {
chatItems: RootState.ChatState;
actions: ChatActions;
}
export interface IBaseLayoutState {}
ChatActions looks like
import { createAction } from 'redux-actions';
import { ChatItemModel } from 'app/models';
export namespace ChatActions {
export enum Type {
ADD_CHAT_ITEM = 'ADD_CHAT_ITEM'
}
export const addChatItem = createAction<PartialPick<ChatItemModel, 'text'>>(Type.ADD_CHAT_ITEM);
}
export type ChatActions = Omit<typeof ChatActions, 'Type'>;
That's a problem i had too when i first started with Redux and TypeScript. There is a tricky solution. The connect methode takes alot of generics. I try to explain it with your example.
First of all you have to split the properties of your BaseLayoutUnconnected.
export interface IBaseLayoutStateProps {
chatItems: RootState.ChatState;
}
export interface IBaseLayoutDispatchProps {
actions: ChatActions;
}
export interface IBaseLayoutOwnProps {
// put properties here you want to make available from the connected component
}
export type IBaseLayoutProps = IBaseLayoutOwnProps & IBaseLayoutDispatchProps & IBaseLayoutStateProps
export interface IBaseLayoutState {}
Then you have to fill the generics of the different redux functions.
const mapStateToProps: MapStateToProps<IBaseLayoutStateProps, {}, RootState> = (state: RootState): IBaseLayoutStateProps => ({
chatItems: state.chatItems
})
export const mapDispatchToProps: MapDispatchToPropsFunction<IBaseLayoutDispatchProps, IBaseLayoutOwnProps> = (dispatch: Dispatch, ownProps: IBaseLayoutDispatchProps): IBaseLayoutDispatchProps => ({
actions: bindActionCreators(omit(ChatActions, 'Type'), dispatch)
});
export default connect<IBaseLayoutStateProps , IBaseLayoutDispatchProps, IBaseLayoutOwnProps , RootState>(
mapStateToProps,
mapDispatchToProps
)(BaseLayoutUnconnected as any)
a good source, where you can find all this stuff i wrote and more is this repository
I want to use React-Redux with typeScript to dipatch an action,But when I use mapDisPatchToProps(),I don't know how to define the type of dispatch,There is My code:
This is the component file:
import * as React from 'react';
import Content from '../component/content';
interface Props {
allCityInformation: {'cityName': string, 'img': string}[];
onGetAllCityInformation: Function;
}
class HomePage extends React.Component<Props> {
componentDidMount() {
this.props.onGetAllCityInformation();
}
render() {
return (
<div>
<Content/>
</div>
);
}
}
export default HomePage;
And this is my container file:
import { connect } from 'react-redux';
import HomePage from '../pages/homePage';
export type DispatchGetCityInformation = () => void;
const mapDispatchToProps = (dispatch: DispatchGetCityInformation) => {
return{
onGetAllCityInformation: dispatch(getCityInformation())
};
};
export default connect(() => {return {};
}, mapDispatchToProps)(HomePage);
Now, the error information is :
enter image description here
So,How to solve this problem?
like this
import { Dispatch } from 'redux';
/*other code*/
const mapDispatchToProps = (dispatch: Dispatch<object>) => ({
asyncRequest: (name: string) => dispatch(someAction(name)),
otherAction: () => dispatch(someAction())
});