Add better type checking when connecting components

This commit is contained in:
Jerko Steiner 2019-03-20 16:57:41 +05:00
parent 150a02b344
commit f2e44f477c
8 changed files with 46 additions and 33 deletions

View File

@ -1,7 +1,7 @@
// Maybe this won't be necessary after this is merged:
// https://github.com/Microsoft/TypeScript/pull/29478
export interface IAction<T, ActionType extends string> {
export interface IAction<T = any, ActionType extends string = string> {
payload: Promise<T> | T,
type: ActionType
}

View File

@ -7,7 +7,7 @@ import {Redirect} from '../components/Redirect'
export interface ILoginFormProps {
error?: string
onSubmit: () => Promise<IUser>
onSubmit: () => void
onChange: (name: string, value: string) => void
data: ICredentials
user?: IUser

View File

@ -1,5 +1,5 @@
import {Connector} from '../redux/Connector'
import {ICredentials} from '@rondo/common'
import {INewUser} from '@rondo/common'
import {ILoginState} from './LoginReducer'
import {IStateSelector} from '../redux'
import {LoginActions} from './LoginActions'
@ -7,9 +7,11 @@ import {RegisterForm} from './RegisterForm'
import {bindActionCreators} from 'redux'
import {withForm} from './withForm'
const defaultCredentials: ICredentials = {
const defaultCredentials: INewUser = {
username: '',
password: '',
firstName: '',
lastName: '',
}
export class RegisterConnector extends Connector<ILoginState> {

View File

@ -1,4 +1,5 @@
import React from 'react'
import {IAction} from '../actions'
export interface IComponentProps<Data> {
onSubmit: () => void
@ -9,7 +10,7 @@ export interface IComponentProps<Data> {
}
export interface IFormHOCProps<Data> {
onSubmit: (props: Data) => Promise<void>
onSubmit: (props: Data) => IAction<any, any>
// TODO figure out what would happen if the underlying child component
// would have the same required property as the HOC, like onSuccess?
onSuccess?: () => void
@ -33,7 +34,9 @@ export function withForm<Data, Props extends IComponentProps<Data>>(
handleSubmit = async (e: React.FormEvent) => {
const {clearOnSuccess, onSuccess} = this.props
e.preventDefault()
await this.props.onSubmit(this.state)
const promise = this.props.onSubmit(this.state)
console.log('aaaaaaaaa', promise)
await promise
if (clearOnSuccess) {
this.setState(initialState)
}

View File

@ -35,9 +35,9 @@ export abstract class Connector<LocalState> {
protected wrap<
State,
StateProps,
DispatchProps,
Props
Props,
StateProps extends Partial<Props>,
DispatchProps extends Partial<Props>,
>(
getLocalState: IStateSelector<State, LocalState>,
mapStateToProps: (state: LocalState) => StateProps,

View File

@ -1,25 +1,26 @@
import React from 'react'
import {IAction} from '../actions'
import {ITeam, ReadonlyRecord} from '@rondo/common'
export interface ITeamListProps {
teamsById: ReadonlyRecord<number, ITeam>,
teamIds: ReadonlyArray<number>,
onAddTeam: (params: {name: string}) => Promise<void>
onRemoveTeam: (params: {id: number}) => Promise<void>
onUpdateTeam: (params: {id: number, name: string}) => Promise<void>
onAddTeam: (params: {name: string}) => IAction
onRemoveTeam: (params: {id: number}) => IAction
onUpdateTeam: (params: {id: number, name: string}) => IAction
editTeamId: number
}
export interface ITeamProps {
team: ITeam
editTeamId: number // TODO handle edits via react-router params
onRemoveTeam: (params: {id: number}) => Promise<void>
onUpdateTeam: (params: {id: number, name: string}) => Promise<void>
onRemoveTeam: (params: {id: number}) => IAction
onUpdateTeam: (params: {id: number, name: string}) => IAction
}
export interface IAddTeamProps {
onAddTeam: (params: {name: string}) => Promise<void>
onUpdateTeam: (params: {id: number, name: string}) => Promise<void>
onAddTeam: (params: {name: string}) => IAction
onUpdateTeam: (params: {id: number, name: string}) => IAction
team?: ITeam
}

View File

@ -1,19 +1,19 @@
import React from 'react'
import {IAction} from '../actions'
import {ITeam, IUser, IUserInTeam, ReadonlyRecord} from '@rondo/common'
import {TeamList} from './TeamList'
import {TeamUserList} from './TeamUserList'
// import {Route} from 'react-router-dom'
export interface ITeamManagerProps {
createTeam: (params: {name: string}) => Promise<void>
updateTeam: (params: {id: number, name: string}) => Promise<void>
removeTeam: (params: {id: number}) => Promise<void>
createTeam: (params: {name: string}) => IAction
updateTeam: (params: {id: number, name: string}) => IAction
removeTeam: (params: {id: number}) => IAction
addUser: (params: {userId: number, teamId: number}) => Promise<void>
removeUser: (params: {userId: number, teamId: number}) => Promise<void>
fetchMyTeams: () => void
fetchUsersInTeam: () => void
findUserByEmail: (email: string) => Promise<IUser>
addUser: (params: {userId: number, teamId: number, roleId: number}) => IAction
removeUser: (params: {userId: number, teamId: number}) => IAction
fetchMyTeams: () => IAction
fetchUsersInTeam: (params: {teamId: number}) => IAction
findUserByEmail: (email: string) => IAction<IUser | undefined>
teamsById: ReadonlyRecord<number, ITeam>
teamIds: ReadonlyArray<number>

View File

@ -1,15 +1,17 @@
import React from 'react'
import {IUser, IUserInTeam, ReadonlyRecord} from '@rondo/common'
import {IAction} from '../actions'
const EMPTY_ARRAY: ReadonlyArray<string> = []
export interface ITeamUsersProps {
// fetchMyTeams: () => void,
fetchUsersInTeam: (teamId: number) => void
findUserByEmail: (email: string) => Promise<IUser>
fetchUsersInTeam: (params: {teamId: number}) => IAction
findUserByEmail: (email: string) => IAction
onAddUser: (params: {userId: number, teamId: number}) => Promise<void>
onRemoveUser: (params: {userId: number, teamId: number}) => Promise<void>
onAddUser: (params: {userId: number, teamId: number, roleId: number})
=> IAction<IUserInTeam>
onRemoveUser: (params: {userId: number, teamId: number}) => IAction
teamId: number
userKeysByTeamId: ReadonlyRecord<number, ReadonlyArray<string>>
@ -22,8 +24,12 @@ export interface ITeamUserProps {
}
export interface IAddUserProps {
onAddUser: (params: {userId: number, teamId: number}) => Promise<void>
onSearchUser: (email: string) => Promise<IUser>
onAddUser: (params: {
userId: number,
teamId: number,
roleId: number,
}) => IAction<IUserInTeam>
onSearchUser: (email: string) => IAction<IUser>
teamId: number
}
@ -70,7 +76,7 @@ export class AddUser extends React.PureComponent<IAddUserProps, IAddUserState> {
event.preventDefault()
const {teamId} = this.props
const {email} = this.state
const user = await this.props.onSearchUser(email)
const user = await this.props.onSearchUser(email).payload
if (!user) {
// TODO handle this better via 404 status code
return
@ -78,6 +84,7 @@ export class AddUser extends React.PureComponent<IAddUserProps, IAddUserState> {
await this.props.onAddUser({
teamId,
userId: user.id,
roleId: 1,
})
this.setState({email: '', user: undefined})
@ -111,7 +118,7 @@ export class TeamUserList extends React.PureComponent<ITeamUsersProps> {
}
async fetchUsersInTeam(teamId: number) {
if (teamId) {
await this.props.fetchUsersInTeam(teamId)
await this.props.fetchUsersInTeam({teamId})
}
}
render() {