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())
});
Related
I am using HOC with redux, and I have been running into the error: Type '{}' is missing the following properties from type 'IProps': email, token. The HOC is supposed to inject the props(email and token) and state to the Lower component. But It is not passing them in this case. And the wrapper function (withLogin) does not appear in the react tree components in dev tools.
My HOC containing function (withLogin.tsx) looks like:
import * as React from "react";
import { connect, MapStateToProps } from "react-redux";
import { Dispatch } from 'redux';
import { propagateError } from "./actions";
import { Diff } from 'utility-types';
export interface IWithLoginProps {
email: string;
token: string;
}
type HocProps =
IDispatchProps & {
// here you can extend ConnectedHoc with new props
users: string
};
interface IDispatchProps {
cleanError(): void;
}
export default function WithLogin<T extends IWithLoginProps>(
BaseComponenet
): React.ComponentType<HocProps> {
class HOC extends React.Component<
HocProps,
{
hash: string;
}
> {
constructor(props) {
super(props);
this.state = {
hash: window.top.location.hash
};
}
render() {
return <BaseComponenet {...this.state} {...this.props} />;
}
}
const mapDispatchToProps = (dispatch: Dispatch): IDispatchProps => {
return {
cleanError: () => dispatch(propagateError(null))
};
};
// #ts-ignore
return connect<{}, IDispatchProps, Diff<HocProps, IWithHistoryProps>, {}>(
null,
mapDispatchToProps
)(HOC);
}
And my BaseComponent(App.tsx) looks like:
import React from "react";
import { connect } from "react-redux";
import { withLoginProps } from "./withLogin";
interface IStateProps {
usernames: string[];
}
interface IProps extends IWithLoginProps {}
const App: React.StatelessComponent <IProps & IStateProps> = (props) => {
return (
<div>
{props.users}
</div>
);
}
const mapStateToProps = (state: IRootState): IStateProps => {
return {
usernames: state.users
};
};
export default connect<IStateProps, null, null, IRootState>(
mapStateToProps, null)(withLogin(App));
My index.tsx:
import * as React from 'react';
import App from './App';
const Root: React.StatelessComponent<{}> = () => (
<div>
<Provider store={store}>
<App />
</Provider>
</div>
);
render(<Root />, document.getElementById(renderRoot));
};
Looks like an issue with how you are importing the default import, that is try changing from:
import { withLoginProps } from "./withLogin";
to:
import withLoginProps from "./withLogin";
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
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 have this error and can't really understand what could go wrong when {connect} imported and const mapStateToProps declared:
./src/Movies.js Syntax error: C:/projects/library/src/Movies.js:
Unexpected token (6:8)
6 | const mapStateToProps = (state) => ({
import React, { Component } from "react";
import { connect } from "react-redux";
import MovieItem from "./MovieItem";
class Movies extends Component {
const mapStateToProps = (state) => ({
movies: state.movies;
});
render() {
let movieItems = this.props.movies.map(movie => {
return <MovieItem movie={movie} />;
});
return <div className="Movies">{movieItems}</div>;
}
}
export default connect(mapStateToProps, null)(Movies);
You need to define mapStateToProps function outside of your React component
import React, { Component } from "react";
import { connect } from "react-redux";
import MovieItem from "./MovieItem";
class Movies extends Component {
render() {
let movieItems = this.props.movies.map(movie => {
return <MovieItem movie={movie} />;
});
return <div className="Movies">{movieItems}</div>;
}
}
const mapStateToProps = (state) => ({
movies: state.movies;
});
export default connect(mapStateToProps, null)(Movies);
A class member cannot be declared as a const, var or let. Also since you need to use it outside of the React component only, you should define it separately
I may be missing something, but I can't find any example where connect() wraps a component defined as a class (extending React.Component), it always wraps components defined as a simple function.
A call like this:
connect(mapStateToProps, mapDispatchToProps)(HomeView)
where HomeView extends React.Component, I get a Cannot call a class as a function error.
Any help would be much appreciated.
Edit (sorry for the ammount of code, I don't know what might be relevant):
routes/Home/components/HomeView.js
import React from 'react'
import './HomeView.scss'
class HomeView extends React.Component {
render() {
return (
<div>
<h4>Home</h4>
<div id="g-signin2" data-onsuccess={this.props.signin} />
</div>
)
}
componentDidMount() {
gapi.signin2.render('g-signin2', {
'scope': 'profile email',
'width': 200,
'height': 50,
'longtitle': true,
'theme': 'dark'
});
}
}
HomeView.propTypes = {
signin : React.PropTypes.func.isRequired
}
export default HomeView
routes/Home/modules/home.js
export const HOME_SIGNIN = 'HOME_SIGNIN'
export function signin(newUser) {
return {
type: HOME_SIGNIN,
payload: newUser
}
}
export const actions = {
signin
}
const ACTION_HANDLERS = {
[HOME_SIGNIN] : (state, action) => {
debugger;
return Object.assign({}, state, {user: action.payload});
}
}
const initialState = {
user: null
}
export default function homeReducer(state = initialState, action) {
const handler = ACTION_HANDLERS[action.type];
return handler ? handler(state, action) : state;
}
routes/Home/containers/HomeContainer.js
import {connect} from 'react-redux'
import {signin} from '../modules/home'
import HomeView from '../components/HomeView'
const mapDispatchToProps = {
signin
}
const mapStateToProps = (state) => ({
user: state.user
})
export default connect(mapStateToProps, mapDispatchToProps)(HomeView)
routes/Home/index.js
import HomeContainer from './containers/HomeContainer'
export default (store) => {
component : HomeContainer(store)
}
routes/index.js
import CoreLayout from '../layouts/CoreLayout'
import HomeRoute from './Home'
export const createRoutes = (store) => ({
path : '/',
component : CoreLayout,
indexRoute : HomeRoute(store),
childRoutes : []
})
export default createRoutes
You can wrap a React component, whether it's a class or a functional component, with react-redux connect.
class MyDiv extends React.Component {
render() {
return <div>hi</div>
}
}
export default connect(({ stateStuff }) => ({ stateStuff }))(MyDiv);
you actually are correctly wrapping your component class in connect(). Your problem is elsewhere, in routes/Home/index.js:
import HomeContainer from './containers/HomeContainer'
export default (store) => {
component : HomeContainer(store)
}
the default export of HomeContainer is the higher-order class returned by connect. You're then trying to use HomeContainer as a function here, just like your console error says:
HomeContainer(store)
.