https://www.youtube.com/watch?v=jrKcJxF0lAU
$ npx create-react-app --template typescript react-with-typescript
set typescript as template
App.js -> App.tsx
const [number, setNumber] = useState(5);
const changeNumber = () => { setNumber("10"); }
const [number, setNumber] = useState<number | string>(5);
this type allow both number and string to number state
const [people, setPeople] = useState([{
name: "LeBron James",
url: "",
age: 36,
note: "some string aslfiasef"
}]);
If we hover on state people, we can see the type of state.
const [people, setPeople] = useState([
{
name: "LeBron James",
url: "",
age: 36,
note: "some string aslfiasef"
},
{
name: "Kobe Bryant",
url: "",
age: 42
}
]);
TS automatically specify literal type. the type is an arrray of 2 objects.
It is too specific. So, It needs to be more general.
people.map(person => { console.log(person.height); })
if we access undefined property of object, JS return undefine data. But if we use TS, It raise error because It is not a defined type.
We can define general form of type of state using bracket
const [people, setPeople] = useState<{age: number, name: string, url: string, note: string}[]>([])
But when using interface for defining type, code would be more clean.
interface IState {
people: {
name: string
age: number
url: string
note?: string
}[]
}
question mark on note means that property is optional
interface type can be used like below
const [people, setPeople] = useState<IState["people"]>([])
Interface IState
is a object. We can access property people as a index IState["people"]
. Looks more clean!👍🏻👍🏻
Then, How can we pass the state as props with ts?
We have to pass parent component's state to child. Make a component named List
and give state from App.tsx
like below.
<List people={people}/>
An error occurs because there isn't corresponding parameter in child component List
.
So, we need to define what type of props we're going to accept from parent. We can use exactly same interface that parent component used before and we can set type of props like below.
const List = (props: IProps) => {}
Then, all error gone😎
Both parent and child component check if type is valid.
There is another way to define props's type.
const List: React.FC<IProps> = ({ people }) => {}
Notice that child component List
is a React Function Component
. We can define the child component's type as React.FC
and define props's type in bracket <IProps>
. Before we specify what List
is, TS has no idea what it is. TS just parse the code as JSX:Element
.
Now, we are going to add register feature to the list. We need set of inputs for registering. Set initial state like below
const [input, setInput] = useState({
name: "",
age: "",
note: ""
});
and define handleChange
function like below
const handleChange = (e) => {
setInput({
...input,
[e.target.name]: e.target.value
})
}
We specify name
attribute in input. So, we can control only what we need to handle like [e.target.name]: e.target.value
.
We got an error on the parameter e
on function handleChange
. It can be solved by setting type of e
as any
but it isn't what we want.
Set onchange
property as onChange={(e)=> {}}
and hover on e
on that. We can get a type of e
which is React.ChangeEvent<HTMLInputElement>
.
Reset the onchange
property as what it was. Just copy and paste the type to parameter e
.
When we set a onChange
attribute in textarea
tag. An error occurs because parameter e
is incompatible. log is like below
Type 'ChangeEvent' is not assignable to type 'ChangeEvent'.
It means we have to allow type ChangeEvent<HTMLTextAreaElement>
to parameter e
. Set type of e
as union type
const handleChange = (e :React.ChangeEvent<HTMLInputElement> | React.ChangeEvent<HTMLTextAreaElement>): void => {
Don't forget to set return type is void
because it does not return anything.
Now, we have to activate the button add to list
. To add person to list, we have to use setPeople
which is defined in app.tsx
we can export our interface defined in app.tsx
.
add import line import {IState as IProps} from '../App';
in List.tsx
import {IState as Props} from '../App';
We can define nested type in AddToList.tsx
import {IState as Props} from '../App';
interface IProps {
people: Props["people"]
setPeople: React.Dispatch<React.SetStateAction<Props["people"]>>
}
handleClick
is like below.
const handleClick = (): void => {
if(
!input.name ||
!input.age
) {
return
}
setPeople([
...people,
{
name: input.name,
age: parseInt(input.age),
note: input.note
}
])
setInput({
name: "",
age: "",
note: "",
})
}