I learned how to implement a simple Todolist Demo with React + Redux, which was pretty easy. Recently, I read some typescript tutorials and decided to re-implement typescript, but it wasn’t that easy. Encountered a lot of potholes. Record
Native JavaScript component writing
import React, { Component } from 'react';
class ComponentName extends Component {
render() {
return (
<div>hello World</div>
) }
}
export default ComponentName;
Copy the code
Typescript component writing
interface IPorps{ name:string}
interface IState{ count:number}
class ComponentName extends Component<IProps,IState> {
render() {
return (
<div>hello World</div>
) }
}
export default ComponentName;
Copy the code
<IProps,IState> ts Component <IProps,IState> IState means that to pass in a component, props and state must have a value of the type constrained by IProps,IState, and a method of the same return type
interface mustHasLength {
length:number
}
function printArgLenth <T extend mustHasLength> (arg:T) : void {
console.log(arg.length)
}
letArr = [1,2,3], objWidthOutLength = {id:1, name:'obj1'
},
objWidthLength = {
id:2,
name:'obj2',
length:3
}
console.log(printArgLength(arr)) // 3 console.log(printArgLength(objWidthLength)) // 3 console.log(printArgLength(objWidthOutLength)) {id: number; name: string; } cannot be assigned to arguments of type "mustHasLength". Property'length' is missing in type '{ id: number; name: string; } ' but required in type 'mustHasLength'.ts(2345)
Copy the code
If arg does not contain length, the compile fails, and the code fails. If arg does not contain length, the compile fails, and the code fails. This makes us think about code organization and definition
In TS, type checking is very strict, and if the type passed to the component does not match the type declared in square bracket generics, the compilation will not pass. Before writing a component, decide what properties and state the component needs to use. Then define the interface, including the types of props and state to use.
import React, { MouseEvent, ChangeEvent, PureComponent } from 'react'
import { TodosState, TodoTypes } from './types'
import { connect } from 'react-redux'
import { addTodo, change_input_value } from './actions'
interface PropsFromDispatch {
addTodo: typeof addTodo
change_input_value: typeof change_input_value
}
interface PropsFromState {
inputValue: string
data: TodoTypes[]
}
type AllProps = PropsFromDispatch & PropsFromState
class AddToto extends PureComponent<AllProps> {
render() {
return (
<div>
<input type='text' onChange={this.handleChange} />
<button type='button' onClick={this.handleSubmit}>
submit todo
</button>
</div>
)
}
handleSubmit = (e: MouseEvent): void => {
if(this.props.inputValue ! = =' ') {
this.props.addTodo(this.props.inputValue)
}
}
handleChange = (e: ChangeEvent<HTMLInputElement>): void => {
let value = e.currentTarget.value
this.props.change_input_value(value)
}
}
const mapDispatchToProps = {
addTodo,
change_input_value
}
const mapStateToProps = (state: TodosState) => {
return {
inputValue: state.inputValue,
data: state.data
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(AddToto)
Copy the code
In my AddTodo component, I used the React-Redux connect method to mount properties and methods from redux to the props of the component, so I defined two PropsFromDispatch interfaces PropsFromState, and then pass the combined type to the component via AllProps so that the component knows what type of methods and properties to use in the future.
types.ts
export interface TodoTypes {
id: number
name: string
}
export interface TodosState {
readonly inputValue: string
readonly data: TodoTypes[]
}
exportinterface TodoDispatch { addTodo? : () => void change_input_value? : () => void deleteToDo? : () => void }export enum TodoActionType {
ADD_TODO = 'ADD_TODO',
DELETE_TODO = 'DELETE_TODO',
CHANGE_INPUT_VALUE = 'CHANGE_INPUT_VALUE'
}
Copy the code
Here defines the three interfaces respectively, TodoTypes, TodosState, TodoDispatch to notify the reducer is about to use the type of the state, and the use of enumerated types (enum) define redux of logo (actionTypes).
reducer.ts
import { Reducer } from 'redux'
import { TodoActionType, TodosState } from './types'
const initialState: TodosState = {
inputValue: ' ',
data: [
{ id: 0, name: 'Sleep' },
{ id: 1, name: 'Sleep' },
{ id: 2, name: 'Sleep' } ]
}
const reducer: Reducer<TodosState> = (state = initialState, action) => {
let newState = JSON.parse(JSON.stringify(state))
switch (action.type) {
case TodoActionType.ADD_TODO:
return addtodo()
case TodoActionType.CHANGE_INPUT_VALUE:
return changeInput()
case TodoActionType.DELETE_TODO:
return deleteTodo()
default:
return newState
}
}
export { reducer as todosReducer }
Copy the code
In reducer. Ts, use the TodosState interface to define the initial state type, and then run different functions according to different behavior identification (TodoActionType). These three functions are used to return the new state.
actions.ts
import { action } from 'typesafe-actions'
import { TodoActionType } from './types'
export const addTodo = (payload: string) => action(TodoActionType.ADD_TODO, payload)
export const deleteTodo = (payload: number) => action(TodoActionType.DELETE_TODO, payload)
export const change_input_value = (payload: string) => action(TodoActionType.CHANGE_INPUT_VALUE, payload)
Copy the code
In actions.ts, the third-party typesafe-Actions library is used to create type-safe actions
As you can see from the above three files, there is a lot more type definition or specification in typescript than in native writing. Type is important to the concept, and compilation will not pass without explicitly telling the compiler the type.
When I first started writing typescript and React, I was not used to it. I had to write a few sentences about debugging and failed to compile. After referring to other people’s projects on the Internet, I gradually understood typescript type concepts and basically got started typescript.