중요한 부분만 훑어보자.
import React, { useState, Dispatch,SetStateAction, ChangeEventHandler } from 'react' // 중요 1
import './App.css'
import { gql, useQuery, NetworkStatus } from '@apollo/client'
const GET_DOGS = gql`
{
dogs{
id
breed
}
}
`
interface GetDogListProps{
setBreed:Dispatch<SetStateAction<string>> // 중요 2
}
function GetDogList({setBreed}:GetDogListProps){
const { loading, error, data } = useQuery(GET_DOGS)
if(loading) return <h1>Loading...</h1>
if(error) return <h1>{error.message}</h1>
function onChange(e:React.ChangeEvent<HTMLSelectElement>){ // 중요 3
console.log('show e: ',e.target.value)
setBreed(e.target.value)
}
return(
<select onChange={onChange}>
{
data.dogs.map((dog:{id:string,breed:string})=>(
<option key={dog.id}>
{dog.breed}
</option>
))
}
</select>
)
}
const GET_DOG_PHOTO = gql` // 중요 4
query dog($breed:String!){
dog(breed:$breed){
id
displayImage
}
}
`
interface ShowImageProp{
breed:string,
}
function ShowImage({breed}:ShowImageProp){
const {loading, error, data, refetch, networkStatus} = useQuery(GET_DOG_PHOTO,{
variables:{breed},
notifyOnNetworkStatusChange:true
})
console.log('networkStatus: ',networkStatus)
if(networkStatus === NetworkStatus.refetch) return <h1>Refetching...</h1>
if(loading) return <h4>Loading...</h4>
if(error) return <h4>something wrong...</h4>
return(
<>
<img
src={data.dog.displayImage}
style={{width:100,height:80}} />
<button onClick={()=>refetch({
breed:'dalmatian'
})}>Refetch! dalmatian</button>
</>
)
}
function App(){
const [breed, setBreed] = useState<string>('')
return(
<div className='App-header'>
<h1>{breed}</h1>
<ShowImage breed={breed} />
<GetDogList setBreed={setBreed}/>
</div>
)
}
export default App
import React, { useState, Dispatch,SetStateAction, ChangeEventHandler } from 'react' // 중요 1
Dispatch
Dispatch는 useState의 setter 부분에서 사용되는 타입 선언이다. (아래와 같음)
function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
SetStateAction과 Dispatch는 다시 아래와 같다.
type SetStateAction<S> = S | ((prevState: S) => S);
type Dispatch<A> = (value: A) => void;
위 타입을 나열한 목적은 다음과 같다.
자식 component에 useState의 setter를 넘겨주고 싶기 때문이다.
function App(){
const [breed, setBreed] = useState<string>('')
return(
<div className='App-header'>
<h1>{breed}</h1>
<ShowImage breed={breed} />
<GetDogList setBreed={setBreed}/>
</div>
)
}
GetDogList는 setBreed라는 setter를 전달 받았다.
App component 자체에서는 에러가 발생하지 않는다.
interface GetDogListProps{
setBreed:Dispatch<SetStateAction<string>> // 중요 2
}
function GetDogList({setBreed}:GetDogListProps){
const { loading, error, data } = useQuery(GET_DOGS)
if(loading) return <h1>Loading...</h1>
if(error) return <h1>{error.message}</h1>
function onChange(e:React.ChangeEvent<HTMLSelectElement>){ // 중요 3
console.log('show e: ',e.target.value)
setBreed(e.target.value)
}
자식 component인 GetDogList는 전달 받은 props인 setter를 어떻게 타입 정의해야 할까?
function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
위 타입 정의에 따르면 setter는 Dispatch<SetStateAction<S>>
타입을 가진다.
따라서 props는 파일의 최상단에서 선언한 Dispatch,SetStateAction,
타입을 Dispatch<SetStateAction<S>>
형식에 맞게 넣어주면 된다.
내가 받아온 breed라는 type은 string이므로 string을 넣어주었다.
React.ChangeEvent<HTMLSelectElement>
해당 타입은 vscode에서 자동적으로 추천해주기는 할 것이다.
하지만 select>option의 onChange의 argument 타입은 이 것이다.
const GET_DOG_PHOTO = gql` // 중요 4
query dog($breed:String!){ // #1
dog(breed:$breed){ // #2
id // #3
displayImage
}
}
`
graphql은 사용하기 복잡하기 때문에 문법을 잘 이해해야 한다.
query dog($breed:String!)
은 query를 고른다