Redux Connect with Typescript - TS2347 - reactjs

I'm new to Typescript and though it is fascinating and a life-saver, this error is quite a hard nut to crack.
TypeScript error: Untyped function calls may not accept type arguments. TS2347
Can you please tell me what should be improved in the below class to get rid of this error ?
Here is the whole class
import React, { FunctionComponent } from 'react'
import { ListGroup } from 'react-bootstrap'
import { connect } from 'react-redux'
type StateProps = {
mbzArtists: IMBZArtist[],
releaseArtistID: string
}
type DispatchProps = {
findMBZReleases: (artistID: string) => void,
}
type OwnProps = {}
type MBZSearchResultsProps = StateProps & DispatchProps & OwnProps
const MBZSearchResults: FunctionComponent<MBZSearchResultsProps> = ({ findMBZReleases, mbzArtists, releaseArtistID }) => {
return (
<div className="MBZSearchResults">
// div content
</div>
)
}
const mapStateToProps = (state: AppState) => {
return {
mbzArtists: state.musicBrainz.mbzArtists,
releaseArtistID: state.musicBrainz.artistReleaseID
}
}
const mapDispatchToProps = (dispatch: any): DispatchProps => {
return {
findMBZReleases: (artistID: string) => dispatch(Actions.MBZActions.findMBZReleases(artistID))
}
}
export default connect<StateProps, DispatchProps, OwnProps>(mapStateToProps, mapDispatchToProps)(MBZSearchResults)
In case you require more information, please let me know.
Thanks.

The issue seems to be fixed by changing the export statement as below:
const component: React.FunctionComponent<OwnProps> =
connect(mapStateToProps, mapDispatchToProps)(MBZSearchResults)
export default component
Can I please have a comment from typescript users to let me know if this is the way to go ?

As stated from the error you posted.
TypeScript error: Untyped function calls may not accept type
arguments. TS2347
The connect function dosen't accept type arguments.
Change the export connect to this:
export default connect(mapStateToProps, mapDispatchToProps)(MBZSearchResults)

Related

Type InferableComponentEnhancerWithProps is missing the following properties from type Component<OwnProps>

I'm following this tutorial to build a chat app with React and Redux on Typescript, but I'm getting an error when exporting my connected component:
Type 'InferableComponentEnhancerWithProps<ConnectedState & ConnectedDispatch, OwnProps>'
is missing the following properties from type 'Component<OwnProps, {}, any>':
context, setState, forceUpdate, render, and 3 more.
This is the code from the tutorial. In which sense should I update it to get rid of this error and be able to use the component normally.
import * as React from 'react'
import * as redux from 'redux'
import { connect } from 'react-redux'
import { Message as MessageModel, UserMessage} from 'rcserver/src/models'
import { ChatState } from '../state'
import { Action } from '../actions'
import { Messages } from './Messages'
import { ChatInput } from './ChatInput'
interface OwnProps {
socket: WebSocket,
username: string
}
interface ConnectedState {
messages: MessageModel[]
}
interface ConnectedDispatch {}
interface OwnProps {}
const mapStateToProps = (state: ChatState, ownProps: OwnProps): ConnectedState => ({
messages: state.messages
})
const mapDispatchToProps = (dispatch: redux.Dispatch<Action>): ConnectedDispatch => ({})
export class ChatAppComponent extends React.Component<ConnectedState & ConnectedDispatch & OwnProps> {
sendHandler = (message: string) => {
const messageObject: MessageModel = {
name: this.props.username,
message: message
}
this.props.socket.send(JSON.stringify(messageObject))
}
render() {
return (
<div className="container">
<h3>React Chat App</h3>
<Messages username={this.props.username} messages={this.props.messages} />
<ChatInput onSend={this.sendHandler} />
</div>
)
}
}
export const ChatApp: React.Component<OwnProps> = connect(mapStateToProps, mapDispatchToProps)
//This last line is the one triggering the error
It's almost correct, but you forgot to apply connect(mapStateToProps, mapDispatchToProps) to your ChatAppComponent. This should work:
export const ChatApp = connect(mapStateToProps, mapDispatchToProps)(ChatAppComponent)
The type of ChatApp will be inferred correctly, so you don't need a type signature. If you do want the signature, you'll need React.FunctionComponent<OwnProps> though, as the connected component is not a class. Alternatively, you could also use the more general React.ComponentType<OwnProps> which works for both classes and function components.

How to use props from connect without having to explicitly write them when rendering component?

I am using redux/connect to get state and actions creators as props in my component. This, however, causes an error with Typescript when rendering the component as the props i'm receiving via connect are not explicitly written.
Is there a way to get around this without making the types optional or is there something else I am missing?
//FetchData Component
import React, {Component} from 'react';
import { connect } from "react-redux";
import {
getData,
filterMaterial,
filterSize,
resetMap
} from "../js/actions/index";
import { MapProperties } from '../types/state';
import { Appstate } from '../js/store';
type DispatchProps = {
getData: () => void,
filterMaterial: (name:string) => void,
filterSize: (name:string) => void,
resetMap: () => void
}
type Props = DispatchProps & LinkStateProps
class FetchData extends Component<Props> {
constructor(props: Props) {
super(props);
this.handleReset = this.handleReset.bind(this)
}
handleReset = () => {
if(this.props.resetMap) this.props.resetMap();
}
componentDidMount() {
if (this.props.getData) this.props.getData()
}
render() {
return (
...
);
}
}
type LinkStateProps = {
geoJSON: MapProperties,
mapJSON: MapProperties
}
const mapStateToProps = (state: Appstate, props:Props):LinkStateProps => {
return {
geoJSON: state.geoJSON,
mapJSON: state.mapJSON
};
};
export default connect(mapStateToProps, {
getData,
filterMaterial,
filterSize,
resetMap
})(FetchData);
//App Component
import React from 'react';
import './App.scss';
import FetchData from './components/FetchData';
function App() {
return (
<div className="App">
//error = Type '{}' is missing the following properties from type 'DispatchProps': getData, filterMaterial, filterSize, resetMap
<FetchData />
</div>
);
}
export default App;
It's because you're passing Props into your mapToState function.
try:
- const mapStateToProps = (state: Appstate, props:Props):LinkStateProps => {
+ const mapStateToProps = (state: Appstate):LinkStateProps => {
return {
geoJSON: state.geoJSON,
mapJSON: state.mapJSON
};
};
The second argument to mapToState is an optional ownProps argument that is only needed if you need any of the component's "own" props (not connected props) to create the proper mapping from state to props. Providing Props as you did makes TypeScript assume that those props must be provided explicitly when using the component, hence the error you were seeing.
Also, you might consider using some built-in features that TypeScript offers to save you from typing things that TypeScript can infer types from automatically.
Consider this:
import React, {Component} from 'react';
import { connect } from "react-redux";
import {
getData,
filterMaterial,
filterSize,
resetMap
} from "../js/actions/index";
import { MapProperties } from '../types/state';
import { Appstate } from '../js/store';
const mapStateToProps = (state: Appstate) => ({
geoJSON: state.geoJSON,
mapJSON: state.mapJSON
});
const dispatchProps = {
getData,
filterMaterial,
filterSize,
resetMap
}
// You could do:
// type LinkStateProps = ReturnType<typeof mapStateToProps>;
// type DispatchProps = typeof dispatchProps;
// type Props = LinkStateProps & DispatchProps;
// or in one line:
type Props = ReturnType<typeof mapStateToProps> & typeof dispatchProps;
class FetchData extends Component<Props> {
constructor(props: Props) {
super(props);
this.handleReset = this.handleReset.bind(this)
}
handleReset = () => {
if(this.props.resetMap) this.props.resetMap();
}
componentDidMount() {
if (this.props.getData) this.props.getData()
}
render() {
return (
<p>something</p>
);
}
}
export default connect(mapStateToProps, dispatchProps)(FetchData);
Creating an object named dispatchProps (which you do anyway to supply to connect) lets you use Typescript's typeof operator to auto-generate the type signature. And since mapStateToProps returns an object, you can use ReturnType<typeof mapStateToProps> to get the type signature of that object. Combining these two with the '&' operator gives you your Props type. It's essentially a more concise (and less error-prone) way to do the exact same thing you were doing.

function not found after mapDispatchToProps

Hi to all react geniuses. I am a newbie and I am trying to achieve a very simple thing here.
Below is the code, which tries to call a function sentTheAlert() on button click. However, I get error in my console.
import React from 'react';
import { connect } from 'react-redux';
import { Button } from 'reactstrap';
import { RouteComponentProps } from 'react-router-dom';
export interface IFancyAlerterProps extends StateProps, DispatchProps, RouteComponentProps<{}> {}
export class FancyAlerter extends React.Component<IFancyAlerterProps> {
handleSubmit= () => {
this.props.sendTheAlert('hello');
};
render() {
return (
<div>
<h1>Today Fancy Alert is {this.props.fancyInfo}</h1>
<Button color="primary" onClick={this.handleSubmit}>
See my Alert
</Button>
</div>
);
}
}
const SEND_MESSAGE = 'SEND_MESSAGE';
interface SendAlertType {
type: typeof SEND_MESSAGE;
payload: string;
}
function sendTheAlert(newMessage: string): SendAlertType {
return {
type: SEND_MESSAGE,
payload: newMessage,
};
}
const mapDispatchToProps = { sendTheAlert };
function mapStateToProps(state) {
return { fancyInfo: 'Fancy this:' + state.currentFunnyString };
}
type StateProps = ReturnType<typeof mapStateToProps>;
type DispatchProps = typeof mapDispatchToProps;
export default connect(mapStateToProps, mapDispatchToProps)(FancyAlerter);
Note: If this information helps, I create a jhispter application with react UI. And tried to add a new component (FancyAlerter). All the old components are able to get my function however, the new component is unable to get this function or any other function.
So, I just don't understand the mechanics I believe. Any help would be really appreciated.
UPDATE: In the above code, the props contains methods from RouteComponentProps but not from the other two types.
It looks like problem about using object for mapDispatchToProps. When you use mapDispatchToProps as an Object you should provide action creator, not void function :
const SEND_MESSAGE = 'SEND_MESSAGE'
interface SendAlertType {
type: typeof SEND_MESSAGE
payload: String
}
function sendTheAlert(newMessage: String): SendAlertType {
return {
type: SEND_MESSAGE,
payload: newMessage
}
}
const mapDispatchToProps = { sendTheAlert };
Later on you can fire an alert on middleware (saga, thunk, etc.).
Check for the usage : https://daveceddia.com/redux-mapdispatchtoprops-object-form/
Test your code : https://codesandbox.io/s/icy-lake-h3rxr?file=/src/CounterMapDispatchObj.js
Thanks for looking into the issue. I figured it out. Of course all your answers helped me eliminate the possible causes.
Seems like how a component is imported makes a lot of difference i the react router file where all the routes were defined.
Supposedly below is my route
<ErrorBoundaryRoute path="/fancy" component={FancyAlerter} />
And the way you import this component is
import FancyAlerter from './modules/fancyalert/fancyalert';
Instead of
import { FancyAlerter } from './modules/fancyalert/fancyalert';

Typing mapStateToProps React Redux

I am not able to type the "state" parameter of a mapStateToProps
If I just change state : any instead of state: AppState it works and no error.
But I would like to have a correct typing for my state parameter.
For now, I have this error on the mapStateToProps param of the connect()
No overload matches this call.
The last overload gave the following error.
Argument of type '(state: { quiz: IQuizInitialState; }) => StateProps' is no assignable to parameter of type 'MapStateToPropsParam'.
Cannot assign the type '(state: { quiz: IQuizInitialState; }) => StateProps' to type 'MapStateToPropsFactory'.
Parameters 'state' and 'initialState' are not compatible.
Property 'quiz' is missing in type '{}' but required in type '{ quiz: IQuizInitialState; }'.ts(2769)
interface OwnProps {
}
interface StateProps {
}
interface DispatchProps {
}
type Props = OwnProps & StateProps & DispatchProps;
export class App extends Component<Props> {
render() {
return (
<div>Hey</div>
);
}
}
const mapStateToProps = (state: AppState): StateProps => ({
})
const mapDispatchToProps = (dispatch: ThunkDispatch<{}, {}, AnyAction>): DispatchProps => {
return {
}
}
// The args 'mapStateToProps' generate the error
export default connect<StateProps,DispatchProps,OwnProps>(mapStateToProps, mapDispatchToProps)(App)
This is my rootReducer :
import { combineReducers } from 'redux';
import { QuizReducer } from './quiz';
const rootReducer = combineReducers({
quiz: QuizReducer
});
export type AppState = ReturnType<typeof rootReducer>
export default rootReducer;
And the single reducer is :
import { TYPES } from '../actions/action-types';
import { IQuizListItem, Action } from '../models/index';
import { AnyAction } from 'redux';
export interface IQuizInitialState {
quizListItem: IQuizListItem[]
}
const quizInitialState: IQuizInitialState = {
quizListItem: []
}
export const QuizReducer = (state = quizInitialState, action: AnyAction): IQuizInitialState => {
switch (action.type) {
case TYPES.getQuizListItems:
return {
...state,
quizListItem: (action as Action<IQuizListItem[]>).payload
}
default:
return state
}
}
Thank you by advance guys !
The type of your state is the same type you use for the whole state. since mapStateToProps takes the whole state to pass it down to selectors. in your case I believe this would be the correct type IQuizInitialState.
const mapStateToProps = (state: IQuizInitialState): StateProps => ({
})
EDIT
In your comment you mention IQuizInitialState isnt your whole application state. Then that one is not the one you need. You need a type for the whole application state. To achieve that you could create an interface for every single reducer type meaning your IQuizInitialState but for the other reducers into a single interface.
Ill have to asume here since I dont have your code base but consider
combineReducers({potato: quizReducer, tomato: otherReduzer})
you'll need a type
interface IApplicationState {
potato: IQuizInitialState;
tomato: IOTherInterfaceYouDefinedForThisReducer;
}
your combineReducers will probable look like :
combineReducers<IApplicationState>({
potato: quizReducer,
tomato: otherReduzer
});
I hope you get the idea.
EDIT 2
After reading your last comment I noticed you are asking for the mapStateToProps with two arguments. and you are just defining one. Your connect generics seems wrong then. you should consider the following:
connect<StateProps, DispatchProps, Props, IApplicationState>
where:
StateProps : describes what was returned by mapStateToProps()
DispatchProps: describes what is returned by dispatchToProps()
Props: Your component props
IApplicationState: Represents your Apps Redux whole state

Typescript TS2339: Property 'queryType' does not exist on type 'IntrinsicAttributes' in Typescript + React + Redux app?

I'm using typescript in my react + redux application. One of my component make use of react-redux's connect. The code is something like this:
import * as React from 'react';
import * as Redux from 'redux'
import compose from 'recompose/compose';
import {
connect,
} from 'react-redux';
import withContextId from '../../../../../app/containers/pageTab/contexts/withContextId';
import {
fetchContent,
} from '../../actions/workspaceActions';
import { HomePageQuery } from '../../interfaces';
interface Props extends StateProps, DispatchProps {
queryType: string,
query: string,
contextId: string,
}
interface OwnProps {
queryType: string,
query: string,
contextId: string,
}
class ContentContainer extends React.PureComponent<Props, {}> {
componentDidMount() {
const { props } = this;
props.fetchContent(props.queryType, props.query, props.contextId);
}
render() {
return (
<div>
{'Tiles Container'}
</div>
);
}
}
interface DispatchProps {
fetchContent: (query: string, queryType: string, contextId: string) => void
}
function mapDispatchToProps(dispatch: Redux.Dispatch<any>): DispatchProps {
return {
fetchContent: (query: string, queryType: string, contextId: string) => {
dispatch(fetchContent(query, queryType, contextId))
}
};
}
interface StateProps {
}
function mapStateToProps(state: any): StateProps {
return {};
}
export default compose(
withContextId,
connect<StateProps, DispatchProps, OwnProps>(mapStateToProps, mapDispatchToProps),
)(ContentContainer);
I read this answer and tried separating my StateProps, DispatchProps and OwnProps but it'll still give me this error. How can I resolve this error?
[EDIT]
I'm receiving queryType and query from it's parent as (and these are mandatory props):
I had an issue with the hoc withContextId. Apparently withContextId is written in JS and it doesn't have any typings.
Removing compose and doing below thing helped me solve the issue. A person with more typescript knowledge can get to it and make me understand as I'm still a newbie.
export default connect<StateProps, DispatchProps, OwnProps>(
mapStateToProps, mapDispatchToProps
)(withContextId(ContentContainer));

Resources