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 });
Related
When I type some text in the inputfield it shows some data when I log inside the reducer, also the console.log(state.genreList) outputs an array. But it will not update the mapStateToProps inside the SearchInput.tsx In action and reducer I see the value is being passed correctly, but still confused why it will not pass them to mapStateToProps. Do I miss something?
To give you a better understanding I will add a codesandbox.
link to codesandbox
// Reducer
import { types } from "../actions";
const initialState = {
genreList: [],
videoList: [],
inputValue: ""
};
export const videoList = (state = initialState, action: any) => {
switch(action.type) {
case types.GET_DATA: {
return {
...state,
genreList: [...state.genreList, action.data]
};
}
case types.GET_INPUT_VALUE: {
return {
...state,
inputValue: action.value
}
}
default:
return state;
}
};
export default videoList;
// Component
import React, { useEffect } from 'react';
import { connect, useDispatch, ConnectedProps } from "react-redux";
import { getData, getInput } from "../../actions/index";
import axios from 'axios';
interface Genre {
id: number;
name: string;
}
interface Video {
id: number;
artist: string;
title: string;
release_year: number;
genre_id: number;
image_url: string;
}
interface IProps {
genres?: Genre[];
videos?: Video[];
input_value?: string;
}
export const SearchInput: React.FC<InputProps | IProps> = () => {
const dispatch = useDispatch();
useEffect(() => {
const fetchData = async () => {
try {
const response = await axios.get('https://raw.githubusercontent.com/XiteTV/frontend-coding-exercise/main/data/dataset.json');
dispatch(getData(response.data));
} catch (err) {
console.log(err)
}
}
fetchData();
}, [dispatch]);
const passValue = (e: string) => {
dispatch(getInput(e));
}
return (
<div>
<input type="text" onChange={(e) => passValue(e.target.value)}/>
<div>Search Input</div>
</div>
)
}
function mapStateToProps(state: any){
// why I dont see anything here????
console.log(state);
return {
genres: state.genreList,
}
}
const connector = connect(mapStateToProps);
type InputProps = ConnectedProps<typeof connector>;
export default connect(SearchInput);
Issues
You are importing the named import, i.e. the unconnected, undecorated SearchInput component.
import { SearchInput } from './components/SearchInput/SearchInput';
You don't connect SearchInput to your redux store correctly.
const connector = connect(mapStateToProps);
type InputProps = ConnectedProps<typeof connector>;
export default connect(SearchInput); // <-- mapStateToProps not used
Solution
Connect SearchInput to redux.
const connector = connect(mapStateToProps); // <-- use connector
type InputProps = ConnectedProps<typeof connector>;
export default connector(SearchInput); // <-- here
Default import the connected component.
import SearchInput from './components/SearchInput/SearchInput';
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);
}
I am trying to make Redux-Thunk work in a TypeScript React application. Unfortunately I am slamming into this error and I've been exhausting every resource I can find trying to find an answer for this error.
I am genuinely stuck and hoping someone can assist.
Error:
Argument of type 'typeof App' is not assignable to parameter of type 'ComponentType<Matching<{ todos: TodoItem[]; loading: boolean; } & typeof import("C:/Users/cevan/Documents/reactFinalShow/frontend-app/src/store/actions"), ClassAttributes<App> & IAppProps & DispatchProps>>'.
Type 'typeof App' is not assignable to type 'ComponentClass<Matching<{ todos: TodoItem[]; loading: boolean; } & typeof import("C:/Users/cevan/Documents/reactFinalShow/frontend-app/src/store/actions"), ClassAttributes<App> & IAppProps & DispatchProps>, any>'.
Types of parameters 'props' and 'props' are incompatible.
Type 'Matching<{ todos: TodoItem[]; loading: boolean; } & typeof import("C:/Users/cevan/Documents/reactFinalShow/frontend-app/src/store/actions"), ClassAttributes<App> & IAppProps & DispatchProps>' is not assignable to type 'Readonly<Props>'.
The types returned by 'getTodoItems()' are incompatible between these types.
Type '(dispatch: Dispatch<AnyAction>) => Promise<string | void | TodoItem[]>' is missing the following properties from type 'TodoItem[]': pop, push, concat, join, and 27
more. TS2345
My code
App.tsx
import React, { Component } from 'react'
import { connect } from 'react-redux';
import * as TodoItemActions from './store/actions'
import { RootState } from './store';
import { ThunkDispatch } from 'redux-thunk';
import { AnyAction, Dispatch, bindActionCreators } from 'redux';
import { GotTodoItems } from './store/types';
import { TodoItem } from './models/TodoItem';
export interface IAppProps {
todos: TodoItem[];
loading: boolean;
}
type Props = IAppProps & DispatchProps;
export class App extends React.Component<Props> {
public render() {
let { getTodoItems } = this.props;
let idk = getTodoItems();
console.log(idk);
return (
<div>
</div>
);
}
}
const mapStateToProps = (state: RootState) => {
console.log(state);
return {
todos: state.todoReducer.todoItems,
loading: state.todoReducer.loading
};
}
const mapDispatchToProps = (dispatch: Dispatch) => bindActionCreators(TodoItemActions, dispatch);
interface DispatchProps {
getTodoItems: () => TodoItem[];
}
export default connect(
mapStateToProps, //bring in the mapStateToProps function to inform redux of what you want to bring in and how to bring it in (to props)
mapDispatchToProps
//bring in specific actions into this component
)(App);
actions.ts (from my store)
import { TodoActionTypes, GETTING_TODO_ITEMS, GOT_TODO_ITEMS } from "./types";
import { TodoItem } from "../models/TodoItem";
import agent from "../api/agent";
import { Dispatch } from 'redux';
import { AppActions } from "../models/actions";
import { store } from '../index';
const requestTodoItems = (): AppActions => {
return {
type: GETTING_TODO_ITEMS,
todoItems: []
}
}
export function getTodoItems() {
return (dispatch: Dispatch) => {
dispatch(requestTodoItems());
let errorEncountered = false;
let hasErrored = false;
let response = agent.ToDoApis.getList(new URLSearchParams);
return response.then(
value => {
//success
console.log(value)
return value;
},
reason => {
//rejection
console.log(reason)
}
).catch(error => `Error when fetching to do items ${error}`)
}
}
// agent.ToDoApis.getList
// .(
// (response) => response.json(),
// (error) => {
// hasErrored = true;
// console.log(error);
// dispatch(requestTodoItems());
// }
// )
// .then((json) => {
// if (!hasErrored) {
// dispatch(returnTodoItems(json));
// }
// });
// return agent.ToDoApis.getList(new URLSearchParams)
// .then(
// (response) => response.IsDone,
// (error) => {
// errorEncountered = true;
// console.log(error);
// dispatch(requestTodoItems());
// }
// )
// .then((json) => {
// if(!errorEncountered){
// dispatch(returnTodoItems(json))
// }
// })
// export async function getTodoItems(isDone: boolean): Promise<TodoActionTypes> {
// console.log("ABOVE");
// const response = await agent.ToDoApis.getList;
// console.log("ABOVE");
// console.log(response);
// console.log("ABOVE");
// dispatch({
// type: GET_TODO_ITEMS,
// isDone: isDone
// });
// }
// export function createNewTodo(todoItem: TodoItem): TodoActionTypes {
// return {
// type: CREATE_NEW_TODO,
// todoItem: todoItem
// }
// }
// export function toggleDoneStatusForTodo(id: string): TodoActionTypes {
// return {
// type: TOGGLE_DONE_STATUS_FOR_TODO,
// id: id
// }
// }
// export function updateEntireTodo(todoItem: TodoItem): TodoActionTypes {
// return {
// type: UPDATE_ENTIRE_TODO,
// todoItem: todoItem
// }
// }
// export function deleteTodo(id: string): TodoActionTypes {
// return {
// type: DELETE_TODO,
// id: id
// }
```
index.tsx (where I configure the store)
```
import { combineReducers, createStore, applyMiddleware } from "redux"
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk, { ThunkMiddleware } from 'redux-thunk'; //NEW LINE
import { todoReducer } from "./reducers";
import { TodosState } from "./types";
import { AppActions } from "../models/actions";
const rootReducer = combineReducers({
todoReducer: todoReducer
})
export type RootState = ReturnType<typeof rootReducer>;
export default function configureStore() {
const store = createStore(
rootReducer,
composeWithDevTools(
applyMiddleware(
thunk as ThunkMiddleware<TodosState, AppActions>
) //NEW FOR THUNK.
)
);
// thunk as ThunkMiddleware<RootState, AppActions>
return store;
}
```
reducers.ts (My reducer)
```import { TodosState, TodoActionTypes, GETTING_TODO_ITEMS, GOT_TODO_ITEMS } from "./types";
const initialState: TodosState = {
todoItems: [],
loading: false
};
export function todoReducer(state = initialState, action: TodoActionTypes): TodosState {
switch (action.type) {
case GETTING_TODO_ITEMS:
return {
...state,
loading: true
}
case GOT_TODO_ITEMS:
return {
...state,
todoItems: action.todoItems,
loading: true
}
default:
return state;
}
}
```
types.ts (my types file for redux)
```
import { TodoItem } from "../models/TodoItem";
import { Action } from "redux";
export interface TodosState {
todoItems: TodoItem[];
loading: boolean;
}
//Action Type Definitions.
//GET (AKA GET)
export const GETTING_TODO_ITEMS = "GETTING_TODO_ITEMS";
export const GOT_TODO_ITEMS = "GOT_TODO_ITEMS";
//CREATE (AKA POST)
export const CREATING_NEW_TODO = "CREATING_NEW_TODO";
export const CREATED_NEW_TODO = "CREATED_NEW_TODO";
//TOGGLE (AKA PATCH)
export const TOGGLING_DONE_STATUS_FOR_TODO = "TOGGLING_DONE_STATUS_FOR_TODO";
export const TOGGLED_DONE_STATUS_FOR_TODO = "TOGGLED_DONE_STATUS_FOR_TODO";
//UPDATE ENTIRE TODO (AKA PUT)
export const UPDATING_ENTIRE_TODO = "UPDATING_ENTIRE_TODO";
export const UPDATED_ENTIRE_TODO = "UPDATED_ENTIRE_TODO";
//DELETE TODO (AKA DELETE)
export const DELETING_TODO = "DELETING_TODO";
export const DELETED_TODO = "DELETED_TODO";
//Actions. (NOTE ASYNC ACTIONS WITH REDUX THUNK ARE VERY DIFFERENTLY WRITTEN!!!)
//WARNING: Very different from redux actions from before for Async!!!
export interface GettingTodoItems extends Action<typeof GETTING_TODO_ITEMS> {
todoItems: TodoItem[];
}
export interface GotTodoItems extends Action<typeof GOT_TODO_ITEMS> {
todoItems: TodoItem[];
}
export interface CreatingNewTodo extends Action<typeof CREATING_NEW_TODO> {
}
export interface CreatedNewTodo extends Action<typeof CREATED_NEW_TODO> {
}
export interface TogglingDoneStatusForTodo extends Action<typeof TOGGLING_DONE_STATUS_FOR_TODO> {
}
export interface ToggledDoneStatusForTodo extends Action<typeof TOGGLED_DONE_STATUS_FOR_TODO> {
}
export interface UpdatingEntireTodo extends Action<typeof UPDATING_ENTIRE_TODO> {
}
export interface UpdatedEntireTodo extends Action<typeof UPDATED_ENTIRE_TODO> {
}
export interface DeletingTodo extends Action<typeof DELETING_TODO> {
}
export interface DeletedTodo extends Action<typeof DELETED_TODO> {
}
export type TodoActionTypes =
GettingTodoItems | GotTodoItems |
CreatingNewTodo | CreatedNewTodo |
TogglingDoneStatusForTodo | ToggledDoneStatusForTodo |
UpdatingEntireTodo | UpdatedEntireTodo |
DeletingTodo | DeletedTodo;
//Examples of what doesn't work HERE!
// interface CreatingNewTodo {
// type: typeof CREATE_NEW_TODO;
// todoItem: TodoItem;
// }
// interface TogglingDoneStatusForTodo {
// type: typeof TOGGLE_DONE_STATUS_FOR_TODO;
// id: string;
// }
// interface UpdatingEntireTodo {
// type: typeof UPDATE_ENTIRE_TODO;
// todoItem: TodoItem;
// }
// interface DeletingTodo {
// type: typeof DELETE_TODO;
// id: string;
// }
```
In App I have a button that saves a message and another that creates a new component
import React from "react";
import { connect } from "react-redux";
import { AppState } from "./redux/store";
import { ChatState } from "./redux/chat/types";
import { sendMessage } from "./redux/chat/actions";
import Test from './components/test'
interface AppProps { sendMessage: typeof sendMessage; chat: ChatState }
const App: React.FC<AppProps> = (props: AppProps) => {
const { sendMessage } = props;
const AddChat = () => {
sendMessage({ user: "John", message: "Message one", timestamp: new Date().getTime() });
};
const AddNode = () => {
const newNode = new Test();
// ^ Error
};
return(
<React.Fragment>
<button onClick={AddChat}>Add Chat</button>
<button onClick={AddNode}>Add Node</button>
</React.Fragment>
);
}
const addState = (state: AppState) => ({ chat: state.chat });
const ReduxConnect = connect(addState, { sendMessage }) (App)
export { ReduxConnect as App} ;
The error here is
(alias) const Test: ConnectedComponent<typeof Test, Pick<{}, never>>
import Test
----------------
Expected 1 arguments, but got 0.ts(2554)
index.d.ts(324, 10): An argument for 'props' was not provided.
So what is it expecting? How can I find out? The constructor doesn't have props, does it want me to declare some, pass some into the new component or what? I'm just not that familiar with class-based components when it comes to redux
The Test component looks like
import { Component } from 'react'
import { connect } from 'react-redux'
import { AppState } from "../redux/store";
import { ChatState } from "../redux/chat/types";
import { sendMessage } from "../redux/chat/actions";
class Test extends Component {
constructor() {
super();
}
render() {
return null
}
}
const mapDispatchToProps = {
sendMessage
}
export default connect(null, mapDispatchToProps)(Test);
I want to be able to sendMessage and get state from this new component
UPDATE
Changing the class component to this fixes the previous but gives a new error
import { Component } from 'react'
import { connect } from 'react-redux'
import { AppState } from "../redux/store";
import { ChatState } from "../redux/chat/types";
import { sendMessage } from "../redux/chat/actions";
interface AppProps { sendMessage: typeof sendMessage; chat: ChatState }
class Test extends Component {
constructor(props: AppProps) {
super();
}
render() {
return null
}
}
const addState = (state: AppState) => ({ chat: state.chat });
const ReduxConnect = connect(addState, { sendMessage }) (Test)
// ^ Error
export { ReduxConnect as Test} ;
and the error is
Argument of type 'typeof Test' is not assignable to parameter of type 'ComponentType<never>'.
Type 'typeof Test' is not assignable to type 'ComponentClass<never, any>'.
Type 'Test' is not assignable to type 'Component<never, any, any>'.
Types of property 'props' are incompatible.
Type 'Readonly<{}> & Readonly<{ children?: ReactNode; }>' is not assignable to type 'never'.ts(2345)
When I hover over new Test I get
You're connecting your Test component to Redux -- which wants to pass in props -- yet you're not accepting any constructor params, so they're not getting passed in. If you just omit your constructor override I think this will work.
EDIT:
class Test extends Component<{}> {
constructor(props = {}) {
super(props);
}
render() {
return null
}
}
I think this default prop param will now mean you don't have to send in a {} when instantiating Test.
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({
// ...