Importing dispatch actions to React Component assistance - reactjs

I am having trouble importing a dispatch action. The compiler is complaning that :
Type 'Readonly<{ children?: ReactNode; }> & Readonly<{}>' has no property 'onShowError' and no string index signature.
const { onShowError, error, errorMessage } = this.props this is the code that is causing the problem.
I understand there is something wrong with my imports and how React extends the Component, etc but I just can't find the solution. I'm new to TypeScript let alone JavaScript. I just cannot figure out where something is going wrong.
I have tried creating my own interface CustomProps and declaring onShowError is a function but does not work. Not Assignable to {}
import * as React from "react"
import { Dispatch, Action } from "redux"
import { connect } from "react-redux"
import { AppState } from "reducers"
import { showError } from "data/error_handler"
import Snackbar from "material-ui/Snackbar"
import RaisedButton from "material-ui/RaisedButton"
class ErrorHandler extends React.Component {
hideErrorPopup = () => {
this.setState({
error: false,
})
}
public render() {
const { onShowError, error, errorMessage } = this.props
return (
<div>
<RaisedButton
onClick={onShowError}
label="Toggle ErrorHandler"
/>
<Snackbar
bodyStyle={{ backgroundColor: "#ffa000", marginBottom: "5px" }}
open={error}
message={errorMessage}
autoHideDuration={5000}
onRequestClose={this.hideErrorPopup}
/>
</div>
)
}
}
const mapStateToProps = (state: AppState) => ({
errorMsg: state.errorRedux.errorMessage,
error: state.errorRedux.error,
})
const mapDispatchToProps = (dispatch: Dispatch<Action>) => {
return {
onShowError: () => dispatch(showError()),
}
}
export default connect<any>(
mapStateToProps,
mapDispatchToProps
)(ErrorHandler)
Reducer.ts
import { ErrorHandlerProps, ActionTypes } from "./"
const initialState: ErrorHandlerProps = {
error: false,
errorMessage: "",
}
export default (
state: ErrorHandlerProps = initialState,
action: ActionTypes
) => {
switch (action.type) {
case "SHOW_ERROR":
return {
...state,
}
}
}
Interface.ts & index.ts
export interface ErrorHandlerProps {
error: boolean
errorMessage: string
}
import reducer from "./reducer"
export { reducer }
export * from "./actions"
export * from "./interfaces"
actions.ts
export type ActionTypes = {
type: "SHOW_ERROR"
error: boolean
errorMessage: string
}
export const showError = (): ActionTypes => ({
type: "SHOW_ERROR",
error: true,
errorMessage: "[ACTIONS]",
})

You probably want to explicitly specify the shape of your component:
class myClass extends React.Component<PropShape, StateShape>
To get the props working, provide the types of your props (which includes your component's actual props, and the props injected by connect: mapStateToProps and mapDispatchToProps). In this case, you only need the injected props:
class ErrorHandler extends React.Component<
ReturnType<typeof mapStateToProps>
& ReturnType<typeof mapDispatchToProps>
> {
...
}
const mapStateToProps = (state: AppState) => ({
errorMsg: state.errorRedux.errorMessage,
error: state.errorRedux.error,
})
const mapDispatchToProps = (dispatch: Dispatch<Action>) => {
return {
onShowError: () => dispatch(showError()),
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ErrorHandler)
You'll probably also want to include the shape of your state, if you truly intend to keep a separate local component state, although I'm not sure what your final intention there is:
class ErrorHandler extends React.Component<
ReturnType<typeof mapStateToProps>
& ReturnType<typeof mapDispatchToProps>,
IState> {
...
}
interface IState {
error: boolean;
}
See https://github.com/sw-yx/react-typescript-cheatsheet for some common use cases for using React with TypeScript.

If you just want to quickly get around ts' complaint, the as any trick will do:
const { onShowError, error, errorMessage } = this.props as any
To address this properly, you need to pass the CustomProps to your Component:
interface CustomProps {
onShowError: Function;
error: boolean;
errorMessage: string;
}
class ErrorHandler extends React.Component<CustomProps> {
hideErrorPopup = () => {
this.setState({
// ...

Related

Uncaught TypeError: this.props.*** is not a function (React-Redux)

I am working on an existing project and this function already works in the other components except in this component 'ComingSoon' which I have just created.
And I get this error in my code with trying to run it.
Uncaught TypeError: this.props.activateAction is not a function
My Component.tsx
import React from 'react';
import { connect } from 'react-redux';
import { RouteComponentProps } from 'react-router-dom';
import { IRootState } from 'app/shared/reducers';
import { activateAction } from 'app/modules/account/activate/activate.reducer';
export interface IComingProps extends StateProps, DispatchProps, RouteComponentProps<{ key: any }> {}
export class ComingSoon extends React.Component<IComingProps, {}> {
componentDidMount() {
const { key } = this.props.match.params;
this.props.activateAction(key);
}
render() {
return (
...
);
}
}
const mapStateToProps = ({ authentication }: IRootState) => ({
account: authentication.account,
isAuthenticated: authentication.isAuthenticated
});
const mapDispatchToProps = { activateAction };
type StateProps = ReturnType<typeof mapStateToProps>;
type DispatchProps = typeof mapDispatchToProps;
export default connect(mapStateToProps,mapDispatchToProps)(ComingSoon);
activate.reducer.ts :
import axios from 'axios';
import { REQUEST, SUCCESS, FAILURE } from 'app/shared/reducers/action-type.util';
export const ACTION_TYPES = {
ACTIVATE_ACCOUNT: 'activate/ACTIVATE_ACCOUNT',
RESET: 'activate/RESET'
};
const initialState = {
activationSuccess: false,
activationFailure: false
};
export type ActivateState = Readonly<typeof initialState>;
// Reducer
export default (state: ActivateState = initialState, action): ActivateState => {
switch (action.type) {
case REQUEST(ACTION_TYPES.ACTIVATE_ACCOUNT):
return {
...state
};
case FAILURE(ACTION_TYPES.ACTIVATE_ACCOUNT):
return {
...state,
activationFailure: true
};
case SUCCESS(ACTION_TYPES.ACTIVATE_ACCOUNT):
return {
...state,
activationSuccess: true
};
case ACTION_TYPES.RESET:
return {
...initialState
};
default:
return state;
}
};
// Actions
export const activateAction = key => ({
type: ACTION_TYPES.ACTIVATE_ACCOUNT,
payload: axios.get('api/activate?key=' + key)
});
export const reset = () => ({
type: ACTION_TYPES.RESET
});
I forgot a declaration or an import?
Is the approach taken correct?
will i need to configure something in the store?
I found solution by referring to this subject React Redux TypeError: this.props is not a function.
I just removed braces from component import in routes component.
import ComingSoon from './shared/layout/coming-soon/comingSoon';
instead of
import {ComingSoon} from './shared/layout/coming-soon/comingSoon';
I think I found your solution, you need to dispatch your action before connect it
export const activateAction = key => dispatch => {
dispatch({
type: ACTION_TYPES.ACTIVATE_ACCOUNT,
payload: axios.get('api/activate?key=' + key)
});
}
check 1
constructor(props) {
super(props);
}

How do I define component type?

I'm trying to write a function that takes Higher Order Components as a argument and returns component connected with the redux store. But this code have a type error in the last line. How to I define type correctly?
import React from 'react'
import { connect } from 'react-redux'
import { Dispatch } from 'redux'
import { AppState } from '~/store'
import { decrement, increment } from '~/store/home'
export type CountableProps = StateType & DispatchType
type StateType = ReturnType<typeof mapStateToProps>
const mapStateToProps = (state: AppState) => ({
count: state.home.count
})
type DispatchType = ReturnType<typeof mapDispatchToProps>
const mapDispatchToProps = (dispatch: Dispatch) => ({
increment: () => dispatch(increment()),
decrement: () => dispatch(decrement())
})
const createCountableComponent =
<P extends CountableProps>(Component: React.ComponentType<P>) =>
class extends React.Component<P> {
componentDidMount() {
// do something...
}
render() {
return <Component {...this.props} />
}
}
export const countable = <P extends CountableProps>(Component: React.ComponentType<P>) =>
connect(
mapStateToProps,
mapDispatchToProps
)(createCountableComponent(Component))
Type error
TS2345: Argument of type 'typeof ConfigurableComponent' is not assignable to parameter of type 'ComponentType<Matching<CountableProps, P>>'
Explicitly typing the connect function seems to work:
export const countable = <P extends CountableProps>(Component: React.ComponentType<P>) =>
connect<StateType, DispatchType, Omit<P, keyof (StateType & DispatchType)>>(
mapStateToProps,
mapDispatchToProps
)(createCountableComponent(Component) as any);
We can cast createCountableComponent(Component) as any, since we typed connect with correct typings (state, dispatch and ownProps types). This means when you use this component you will only see props that are not Countable (wich is the desired solution).
So:
class Test extends React.Component<{test: string}> {
render() {
return <div />;
}
};
countable(Test)
will give an error, while:
class Test extends React.Component<CountableProps & {test: string}> {
render() {
return <div />;
}
};
const Test2 = countable(Test)
<Test2 /> -> intelisense will only give you test now :)
Hope this helps.

React Redux dispatch on componentDidMount -> property does not exist

I'm working on my first project using React + Redux and am encountering few issues when I try to dispatch a function in the componentDidMount part. I tried to follow the Reddit API example project in the docs but as they use JavaScript and I'm using TypeScript not quite everything is the same.
This is the React component where I'm trying to achieve this:
export class EducationComponent extends Component {
constructor(props: any) {
super(props);
}
componentDidMount() {
const { dispatch, getUser } = this.props;
dispatch(getUser());
}
public render() {
return (
<Content className="component">
<Demo/>
</Content>
);
}
}
function mapStateToProps(state: State) {
const { isLoaded, isFetching, user } = state.userProfile;
return {
user,
isFetching,
isLoaded
}
}
export default connect(mapStateToProps)(EducationComponent)
export const Education = (EducationComponent);
I'm receiving following error in the const { dispatch, getUser } = this.props; line:
Error:(16, 17) TS2339: Property 'dispatch' does not exist on type 'Readonly<{}> & Readonly<{ children?: ReactNode; }>'.
Error:(16, 27) TS2339: Property 'getUser' does not exist on type 'Readonly<{}> & Readonly<{ children?: ReactNode; }>'.
The project can be found here if there are any uncertainties: https://github.com/jLemmings/GoCVFrontend
Is this the right way to get this done or are there better options?
Thanks
EDIT with current state:
const {Content} = Layout;
export class EducationComponent extends Component {
constructor(props: any) {
super(props);
}
componentDidMount() {
this.props.userAction();
}
public render() {
return (
<Content className="component">
<Demo/>
</Content>
);
}
}
const mapDispatchToProps = (dispatch: Dispatch) => bindActionCreators({
userAction: action.getUser,
}, dispatch);
function mapStateToProps(state: State) {
const { isLoaded, isFetching, user } = state.userProfile;
return {
user,
isFetching,
isLoaded
}
}
export default connect(mapStateToProps, mapDispatchToProps)(EducationComponent)
FINAL EDIT (got it working):
interface MyProps {
getUser: () => void
}
interface MyState {
userAction: ThunkAction<Promise<void>, {}, {}, AnyAction>
}
export class EducationComponent extends Component<MyProps, MyState> {
static defaultProps = {getUser: undefined};
constructor(props: any) {
super(props);
}
componentDidMount() {
this.props.getUser()
}
public render() {
return (
<Content className="component">
<Demo/>
</Content>
);
}
}
const mapDispatchToProps = (dispatch: ThunkDispatch<{}, {}, any>, ownProps: MyProps) => {
return {
getUser: () => {
dispatch(getUser());
}
}
}
function mapStateToProps(state: State) {
const {isLoaded, isFetching, user} = state.userProfile;
return {
user,
isFetching,
isLoaded
}
}
export default connect(mapStateToProps, mapDispatchToProps)(EducationComponent)
After looking at your project, you are importing the component like this :
import {Education} from "../pages/Public/Education";
This will import the non connected component that's why you don't have access to dispatch.
You should import the default connected component :
import Education from "../pages/Public/Education";
You can do it like this
componentDidMount() {
this.props.getUser();
}
// mapStateToProps
mapDispatchToProps = (state) => {
getUser: () => dispatch(getUser()); //Don't forget to import getUser from your action creator
}
export default connect(mapStateToProps, mapDispatchToProps)(EducationComponent)
export const Education = (EducationComponent); // Delete this line no need for this because you're already exporting it as default
Reference: https://react-redux.js.org/using-react-redux/connect-mapdispatch#arguments
Do you bind the dispatch method using mapDispatchToProps?

Typescript & Redux connect

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

Cannot set state on react component

For some reason, the onSubmit() function I cannot set the state of isLoading even though I'm pretty sure I have done the same thing in the past.
import * as React from 'react';
import * as Redux from 'redux';
const { connect } = require('react-redux');
import { push } from "react-router-redux";
import { Col, Jumbotron, Row, Well, Label, Button, FormGroup, FormControl } from 'react-bootstrap';
import { ISession, IApplicationState } from "store";
import './styles.scss';
import { FeedComponent, Feed, StorageApiContext } from "api"
import { Content, ContentLoadingWrapper } from "components";
interface IProfileUserPageProps {
session: ISession;
feed: Feed;
feedComponent: FeedComponent;
}
interface IProfileUserPageState {
isLoading: boolean;
}
#connect(
(state: IApplicationState) => ({
session: state.session.data,
}),
(dispatch: Redux.Dispatch<any>) => ({
navigateToLogin: () => dispatch(push("/")),
})
)
export class ProfileUserPage extends React.Component<IProfileUserPageProps, IProfileUserPageState> {
constructor() {
super();
this.state = { isLoading: false };
}
componentWillMount() {
const {
session,
} = this.props;
}
onSubmit() {
this.setState = ({ isLoading: true });
var inputValue = (document.getElementById("competitors-area") as HTMLInputElement).value;
const addTo: string[] = [];
inputValue.split("\n").forEach(company => {
addTo.push(company)
});
const context = new StorageApiContext();
this.props.session.user.Competitors = addTo;
context.Users.modify(this.props.session.user);
}
The error I receive is:
ERROR in [at-loader] ./src/app/pages/profileUser/ProfileUserPage.tsx:38:28
TS2322: Type '{ isLoaded: boolean; }' is not assignable to type '{ <K extends "isLoaded">(f: (prevState: IProfileUserPageState, props: IProfileUserPageProps) => P...'.
Object literal may only specify known properties, and 'isLoaded' does not exist in type '{ <K extends "isLoaded">(f: (prevState: IProfileUserPageState, props: IProfileUserPageProps) => P...'.
setState() is not an object. It is function to be called like that:
this.setState({ isLoading: true });

Resources