Upgrade to Pro — share decks privately, control downloads, hide ads and more …

3つのNext.jsプロジェクトを 新卒エンジニアと一緒に 開発した話

hiroya iizuka
July 30, 2021
370

3つのNext.jsプロジェクトを 新卒エンジニアと一緒に 開発した話

Next.js 開発の話です。

hiroya iizuka

July 30, 2021
Tweet

Transcript

  1. 初期状態 😓 const Home : NextPage = () => {

    const [number, setNumber] = useState("")) const [year, selectYear] = useState("1980"); const [month, selectMonth] = useState("1"); const [day, selectDay] = useState("1"); useEffect(() => { ・・・ }, []); ・・・ return( <div> <div> </div> </div> } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
  2. const Home : NextPage = () => { const [year,

    selectYear] = useState("1980"); const [month, selectMonth] = useState("1"); const [day, selectDay] = useState("1"); useEffect(() => { ・・・ }, []); ・・・ return( <div> <div> </div> </div> } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 } const Container = () => { 1 const [year, selectYear] = useState("1980"); 2 const [month, selectMonth] = useState("1"); 3 const [day, selectDay] = useState("1"); 4 5 6 useEffect(() => { 7 ... 8 }, []); 9 10 return( 11 <Component 12 year = {year} 13 month = {month} 14 day = {day} 15 /> 16 } 17 } 18 19 const Component = ({ 20 year, 21 month, 22 day 23 }: Props) => { 24 return ( 25 <div> 26 27 </div> 28 ) 29 30 Presentation Domain Separation
  3. const Home : NextPage = () => { const [year,

    selectYear] = useState("1980"); const [month, selectMonth] = useState("1"); const [day, selectDay] = useState("1"); useEffect(() => { ・・・ }, []); ・・・ return( <div> <div> </div> </div> } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 } const Container = () => { 1 const [year, selectYear] = useState("1980"); 2 const [month, selectMonth] = useState("1"); 3 const [day, selectDay] = useState("1"); 4 5 6 useEffect(() => { 7 ... 8 }, []); 9 10 return( 11 <Component 12 year = {year} 13 month = {month} 14 day = {day} 15 /> 16 } 17 } 18 19 const Component = ({ 20 year, 21 month, 22 day 23 }: Props) => { 24 return ( 25 <div> 26 27 </div> 28 ) 29 30 const Container = () => { const [year, selectYear] = useState("1980"); const [month, selectMonth] = useState("1"); const [day, selectDay] = useState("1"); useEffect(() => { ... }, []); return( <Component year = {year} month = {month} day = {day} /> } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const Component = ({ 20 year, 21 month, 22 day 23 }: Props) => { 24 return ( 25 <div> 26 27 </div> 28 ) 29 } 30 Presentation Domain Separation
  4. const Home : NextPage = () => { const [year,

    selectYear] = useState("1980"); const [month, selectMonth] = useState("1"); const [day, selectDay] = useState("1"); useEffect(() => { ・・・ }, []); ・・・ return( <div> <div> </div> </div> } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 } const Container = () => { 1 const [year, selectYear] = useState("1980"); 2 const [month, selectMonth] = useState("1"); 3 const [day, selectDay] = useState("1"); 4 5 6 useEffect(() => { 7 ... 8 }, []); 9 10 return( 11 <Component 12 year = {year} 13 month = {month} 14 day = {day} 15 /> 16 } 17 } 18 19 const Component = ({ 20 year, 21 month, 22 day 23 }: Props) => { 24 return ( 25 <div> 26 27 </div> 28 ) 29 30 const Container = () => { const [year, selectYear] = useState("1980"); const [month, selectMonth] = useState("1"); const [day, selectDay] = useState("1"); useEffect(() => { ... }, []); return( <Component year = {year} month = {month} day = {day} /> } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const Component = ({ 20 year, 21 month, 22 day 23 }: Props) => { 24 return ( 25 <div> 26 27 </div> 28 ) 29 } 30 const Component = ({ year, month, day }: Props) => { return ( <div> </div> ) } const Container = () => { 1 const [year, selectYear] = useState("1980"); 2 const [month, selectMonth] = useState("1"); 3 const [day, selectDay] = useState("1"); 4 5 6 useEffect(() => { 7 ... 8 }, []); 9 10 return( 11 <Component 12 year = {year} 13 month = {month} 14 day = {day} 15 /> 16 } 17 } 18 19 20 21 22 23 24 25 26 27 28 29 30 Presentation Domain Separation
  5. const Home : NextPage = () => { const [year,

    selectYear] = useState("1980"); const [month, selectMonth] = useState("1"); const [day, selectDay] = useState("1"); useEffect(() => { ・・・ }, []); ・・・ return( <div> <div> </div> </div> } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 } const Container = () => { 1 const [year, selectYear] = useState("1980"); 2 const [month, selectMonth] = useState("1"); 3 const [day, selectDay] = useState("1"); 4 5 6 useEffect(() => { 7 ... 8 }, []); 9 10 return( 11 <Component 12 year = {year} 13 month = {month} 14 day = {day} 15 /> 16 } 17 } 18 19 const Component = ({ 20 year, 21 month, 22 day 23 }: Props) => { 24 return ( 25 <div> 26 27 </div> 28 ) 29 30 const Container = () => { const [year, selectYear] = useState("1980"); const [month, selectMonth] = useState("1"); const [day, selectDay] = useState("1"); useEffect(() => { ... }, []); return( <Component year = {year} month = {month} day = {day} /> } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const Component = ({ 20 year, 21 month, 22 day 23 }: Props) => { 24 return ( 25 <div> 26 27 </div> 28 ) 29 } 30 const Component = ({ year, month, day }: Props) => { return ( <div> </div> ) } const Container = () => { 1 const [year, selectYear] = useState("1980"); 2 const [month, selectMonth] = useState("1"); 3 const [day, selectDay] = useState("1"); 4 5 6 useEffect(() => { 7 ... 8 }, []); 9 10 return( 11 <Component 12 year = {year} 13 month = {month} 14 day = {day} 15 /> 16 } 17 } 18 19 20 21 22 23 24 25 26 27 28 29 30 const Container = () => { const [year, selectYear] = useState("1980"); const [month, selectMonth] = useState("1"); const [day, selectDay] = useState("1"); useEffect(() => { ... }, []); return( <Component year = {year} month = {month} day = {day} /> } } const Component = ({ year, month, day }: Props) => { return ( <div> </div> ) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 Presentation Domain Separation
  6. 従来 const loadProducts = async(): Promise<Product[]> => { const response

    = await fetch('https://api.mysite.com/products') const products: unknown = await response.json() if (!isArrayOfProducts(products)) { throw new TypeError('Received malformed products API response') } return products } const isArrayOfProducts = (obj: unknown): obj is Product[] => { return Array.isArray(obj) && obj.every(isProduct) } const isProduct = (obj: unknown): obj is Product => { return obj != null && typeof (obj as Product).id === 'string' } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Typescript の ユーザー定義型ガード
  7. 従来 const loadProducts = async(): Promise<Product[]> => { const response

    = await fetch('https://api.mysite.com/products') const products: unknown = await response.json() if (!isArrayOfProducts(products)) { throw new TypeError('Received malformed products API response') } return products } const isArrayOfProducts = (obj: unknown): obj is Product[] => { return Array.isArray(obj) && obj.every(isProduct) } const isProduct = (obj: unknown): obj is Product => { return obj != null && typeof (obj as Product).id === 'string' } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const response = await fetch('https://api.mysite.com/products') const products: unknown = await response.json() const loadProducts = async(): Promise<Product[]> => { 1 2 3 4 if (!isArrayOfProducts(products)) { 5 throw new TypeError('Received malformed products API response') 6 } 7 return products 8 } 9 10 const isArrayOfProducts = (obj: unknown): obj is Product[] => { 11 return Array.isArray(obj) && obj.every(isProduct) 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 Typescript の ユーザー定義型ガード
  8. 従来 const loadProducts = async(): Promise<Product[]> => { const response

    = await fetch('https://api.mysite.com/products') const products: unknown = await response.json() if (!isArrayOfProducts(products)) { throw new TypeError('Received malformed products API response') } return products } const isArrayOfProducts = (obj: unknown): obj is Product[] => { return Array.isArray(obj) && obj.every(isProduct) } const isProduct = (obj: unknown): obj is Product => { return obj != null && typeof (obj as Product).id === 'string' } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const response = await fetch('https://api.mysite.com/products') const products: unknown = await response.json() const loadProducts = async(): Promise<Product[]> => { 1 2 3 4 if (!isArrayOfProducts(products)) { 5 throw new TypeError('Received malformed products API response') 6 } 7 return products 8 } 9 10 const isArrayOfProducts = (obj: unknown): obj is Product[] => { 11 return Array.isArray(obj) && obj.every(isProduct) 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 if (!isArrayOfProducts(products)) { throw new TypeError('Received malformed products API response') } return products const loadProducts = async(): Promise<Product[]> => { 1 const response = await fetch('https://api.mysite.com/products') 2 const products: unknown = await response.json() 3 4 5 6 7 8 } 9 10 const isArrayOfProducts = (obj: unknown): obj is Product[] => { 11 return Array.isArray(obj) && obj.every(isProduct) 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 Typescript の ユーザー定義型ガード
  9. 従来 const loadProducts = async(): Promise<Product[]> => { const response

    = await fetch('https://api.mysite.com/products') const products: unknown = await response.json() if (!isArrayOfProducts(products)) { throw new TypeError('Received malformed products API response') } return products } const isArrayOfProducts = (obj: unknown): obj is Product[] => { return Array.isArray(obj) && obj.every(isProduct) } const isProduct = (obj: unknown): obj is Product => { return obj != null && typeof (obj as Product).id === 'string' } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const response = await fetch('https://api.mysite.com/products') const products: unknown = await response.json() const loadProducts = async(): Promise<Product[]> => { 1 2 3 4 if (!isArrayOfProducts(products)) { 5 throw new TypeError('Received malformed products API response') 6 } 7 return products 8 } 9 10 const isArrayOfProducts = (obj: unknown): obj is Product[] => { 11 return Array.isArray(obj) && obj.every(isProduct) 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 if (!isArrayOfProducts(products)) { throw new TypeError('Received malformed products API response') } return products const loadProducts = async(): Promise<Product[]> => { 1 const response = await fetch('https://api.mysite.com/products') 2 const products: unknown = await response.json() 3 4 5 6 7 8 } 9 10 const isArrayOfProducts = (obj: unknown): obj is Product[] => { 11 return Array.isArray(obj) && obj.every(isProduct) 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 const isArrayOfProducts = (obj: unknown): obj is Product[] => { return Array.isArray(obj) && obj.every(isProduct) } const isProduct = (obj: unknown): obj is Product => { return obj != null && typeof (obj as Product).id === 'string' } const loadProducts = async(): Promise<Product[]> => { 1 const response = await fetch('https://api.mysite.com/products') 2 const products: unknown = await response.json() 3 4 if (!isArrayOfProducts(products)) { 5 throw new TypeError('Received malformed products API response') 6 } 7 return products 8 } 9 10 11 12 13 14 15 16 17 Typescript の ユーザー定義型ガード
  10. 従来 const loadProducts = async(): Promise<Product[]> => { const response

    = await fetch('https://api.mysite.com/products') const products: unknown = await response.json() if (!isArrayOfProducts(products)) { throw new TypeError('Received malformed products API response') } return products } const isArrayOfProducts = (obj: unknown): obj is Product[] => { return Array.isArray(obj) && obj.every(isProduct) } const isProduct = (obj: unknown): obj is Product => { return obj != null && typeof (obj as Product).id === 'string' } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const response = await fetch('https://api.mysite.com/products') const products: unknown = await response.json() const loadProducts = async(): Promise<Product[]> => { 1 2 3 4 if (!isArrayOfProducts(products)) { 5 throw new TypeError('Received malformed products API response') 6 } 7 return products 8 } 9 10 const isArrayOfProducts = (obj: unknown): obj is Product[] => { 11 return Array.isArray(obj) && obj.every(isProduct) 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 if (!isArrayOfProducts(products)) { throw new TypeError('Received malformed products API response') } return products const loadProducts = async(): Promise<Product[]> => { 1 const response = await fetch('https://api.mysite.com/products') 2 const products: unknown = await response.json() 3 4 5 6 7 8 } 9 10 const isArrayOfProducts = (obj: unknown): obj is Product[] => { 11 return Array.isArray(obj) && obj.every(isProduct) 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 const isArrayOfProducts = (obj: unknown): obj is Product[] => { return Array.isArray(obj) && obj.every(isProduct) } const isProduct = (obj: unknown): obj is Product => { return obj != null && typeof (obj as Product).id === 'string' } const loadProducts = async(): Promise<Product[]> => { 1 const response = await fetch('https://api.mysite.com/products') 2 const products: unknown = await response.json() 3 4 if (!isArrayOfProducts(products)) { 5 throw new TypeError('Received malformed products API response') 6 } 7 return products 8 } 9 10 11 12 13 14 15 16 17 return Array.isArray(obj) && obj.every(isProduct) const loadProducts = async(): Promise<Product[]> => { 1 const response = await fetch('https://api.mysite.com/products') 2 const products: unknown = await response.json() 3 4 if (!isArrayOfProducts(products)) { 5 throw new TypeError('Received malformed products API response') 6 } 7 return products 8 } 9 10 const isArrayOfProducts = (obj: unknown): obj is Product[] => { 11 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 Typescript の ユーザー定義型ガード
  11. 従来 const loadProducts = async(): Promise<Product[]> => { const response

    = await fetch('https://api.mysite.com/products') const products: unknown = await response.json() if (!isArrayOfProducts(products)) { throw new TypeError('Received malformed products API response') } return products } const isArrayOfProducts = (obj: unknown): obj is Product[] => { return Array.isArray(obj) && obj.every(isProduct) } const isProduct = (obj: unknown): obj is Product => { return obj != null && typeof (obj as Product).id === 'string' } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const response = await fetch('https://api.mysite.com/products') const products: unknown = await response.json() const loadProducts = async(): Promise<Product[]> => { 1 2 3 4 if (!isArrayOfProducts(products)) { 5 throw new TypeError('Received malformed products API response') 6 } 7 return products 8 } 9 10 const isArrayOfProducts = (obj: unknown): obj is Product[] => { 11 return Array.isArray(obj) && obj.every(isProduct) 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 if (!isArrayOfProducts(products)) { throw new TypeError('Received malformed products API response') } return products const loadProducts = async(): Promise<Product[]> => { 1 const response = await fetch('https://api.mysite.com/products') 2 const products: unknown = await response.json() 3 4 5 6 7 8 } 9 10 const isArrayOfProducts = (obj: unknown): obj is Product[] => { 11 return Array.isArray(obj) && obj.every(isProduct) 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 const isArrayOfProducts = (obj: unknown): obj is Product[] => { return Array.isArray(obj) && obj.every(isProduct) } const isProduct = (obj: unknown): obj is Product => { return obj != null && typeof (obj as Product).id === 'string' } const loadProducts = async(): Promise<Product[]> => { 1 const response = await fetch('https://api.mysite.com/products') 2 const products: unknown = await response.json() 3 4 if (!isArrayOfProducts(products)) { 5 throw new TypeError('Received malformed products API response') 6 } 7 return products 8 } 9 10 11 12 13 14 15 16 17 return Array.isArray(obj) && obj.every(isProduct) const loadProducts = async(): Promise<Product[]> => { 1 const response = await fetch('https://api.mysite.com/products') 2 const products: unknown = await response.json() 3 4 if (!isArrayOfProducts(products)) { 5 throw new TypeError('Received malformed products API response') 6 } 7 return products 8 } 9 10 const isArrayOfProducts = (obj: unknown): obj is Product[] => { 11 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 const isArrayOfProducts = (obj: unknown): obj is Product[] => { const loadProducts = async(): Promise<Product[]> => { 1 const response = await fetch('https://api.mysite.com/products') 2 const products: unknown = await response.json() 3 4 if (!isArrayOfProducts(products)) { 5 throw new TypeError('Received malformed products API response') 6 } 7 return products 8 } 9 10 11 return Array.isArray(obj) && obj.every(isProduct) 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 Typescript の ユーザー定義型ガード
  12. 従来 const loadProducts = async(): Promise<Product[]> => { const response

    = await fetch('https://api.mysite.com/products') const products: unknown = await response.json() if (!isArrayOfProducts(products)) { throw new TypeError('Received malformed products API response') } return products } const isArrayOfProducts = (obj: unknown): obj is Product[] => { return Array.isArray(obj) && obj.every(isProduct) } const isProduct = (obj: unknown): obj is Product => { return obj != null && typeof (obj as Product).id === 'string' } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const response = await fetch('https://api.mysite.com/products') const products: unknown = await response.json() const loadProducts = async(): Promise<Product[]> => { 1 2 3 4 if (!isArrayOfProducts(products)) { 5 throw new TypeError('Received malformed products API response') 6 } 7 return products 8 } 9 10 const isArrayOfProducts = (obj: unknown): obj is Product[] => { 11 return Array.isArray(obj) && obj.every(isProduct) 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 if (!isArrayOfProducts(products)) { throw new TypeError('Received malformed products API response') } return products const loadProducts = async(): Promise<Product[]> => { 1 const response = await fetch('https://api.mysite.com/products') 2 const products: unknown = await response.json() 3 4 5 6 7 8 } 9 10 const isArrayOfProducts = (obj: unknown): obj is Product[] => { 11 return Array.isArray(obj) && obj.every(isProduct) 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 const isArrayOfProducts = (obj: unknown): obj is Product[] => { return Array.isArray(obj) && obj.every(isProduct) } const isProduct = (obj: unknown): obj is Product => { return obj != null && typeof (obj as Product).id === 'string' } const loadProducts = async(): Promise<Product[]> => { 1 const response = await fetch('https://api.mysite.com/products') 2 const products: unknown = await response.json() 3 4 if (!isArrayOfProducts(products)) { 5 throw new TypeError('Received malformed products API response') 6 } 7 return products 8 } 9 10 11 12 13 14 15 16 17 return Array.isArray(obj) && obj.every(isProduct) const loadProducts = async(): Promise<Product[]> => { 1 const response = await fetch('https://api.mysite.com/products') 2 const products: unknown = await response.json() 3 4 if (!isArrayOfProducts(products)) { 5 throw new TypeError('Received malformed products API response') 6 } 7 return products 8 } 9 10 const isArrayOfProducts = (obj: unknown): obj is Product[] => { 11 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 const isArrayOfProducts = (obj: unknown): obj is Product[] => { const loadProducts = async(): Promise<Product[]> => { 1 const response = await fetch('https://api.mysite.com/products') 2 const products: unknown = await response.json() 3 4 if (!isArrayOfProducts(products)) { 5 throw new TypeError('Received malformed products API response') 6 } 7 return products 8 } 9 10 11 return Array.isArray(obj) && obj.every(isProduct) 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 return products const loadProducts = async(): Promise<Product[]> => { 1 const response = await fetch('https://api.mysite.com/products') 2 const products: unknown = await response.json() 3 4 if (!isArrayOfProducts(products)) { 5 throw new TypeError('Received malformed products API response') 6 } 7 8 } 9 10 const isArrayOfProducts = (obj: unknown): obj is Product[] => { 11 return Array.isArray(obj) && obj.every(isProduct) 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 Typescript の ユーザー定義型ガード
  13. 従来 const loadProducts = async(): Promise<Product[]> => { const response

    = await fetch('https://api.mysite.com/products') const products: unknown = await response.json() if (!isArrayOfProducts(products)) { throw new TypeError('Received malformed products API response') } return products } const isArrayOfProducts = (obj: unknown): obj is Product[] => { return Array.isArray(obj) && obj.every(isProduct) } const isProduct = (obj: unknown): obj is Product => { return obj != null && typeof (obj as Product).id === 'string' } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const response = await fetch('https://api.mysite.com/products') const products: unknown = await response.json() const loadProducts = async(): Promise<Product[]> => { 1 2 3 4 if (!isArrayOfProducts(products)) { 5 throw new TypeError('Received malformed products API response') 6 } 7 return products 8 } 9 10 const isArrayOfProducts = (obj: unknown): obj is Product[] => { 11 return Array.isArray(obj) && obj.every(isProduct) 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 if (!isArrayOfProducts(products)) { throw new TypeError('Received malformed products API response') } return products const loadProducts = async(): Promise<Product[]> => { 1 const response = await fetch('https://api.mysite.com/products') 2 const products: unknown = await response.json() 3 4 5 6 7 8 } 9 10 const isArrayOfProducts = (obj: unknown): obj is Product[] => { 11 return Array.isArray(obj) && obj.every(isProduct) 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 const isArrayOfProducts = (obj: unknown): obj is Product[] => { return Array.isArray(obj) && obj.every(isProduct) } const isProduct = (obj: unknown): obj is Product => { return obj != null && typeof (obj as Product).id === 'string' } const loadProducts = async(): Promise<Product[]> => { 1 const response = await fetch('https://api.mysite.com/products') 2 const products: unknown = await response.json() 3 4 if (!isArrayOfProducts(products)) { 5 throw new TypeError('Received malformed products API response') 6 } 7 return products 8 } 9 10 11 12 13 14 15 16 17 return Array.isArray(obj) && obj.every(isProduct) const loadProducts = async(): Promise<Product[]> => { 1 const response = await fetch('https://api.mysite.com/products') 2 const products: unknown = await response.json() 3 4 if (!isArrayOfProducts(products)) { 5 throw new TypeError('Received malformed products API response') 6 } 7 return products 8 } 9 10 const isArrayOfProducts = (obj: unknown): obj is Product[] => { 11 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 const isArrayOfProducts = (obj: unknown): obj is Product[] => { const loadProducts = async(): Promise<Product[]> => { 1 const response = await fetch('https://api.mysite.com/products') 2 const products: unknown = await response.json() 3 4 if (!isArrayOfProducts(products)) { 5 throw new TypeError('Received malformed products API response') 6 } 7 return products 8 } 9 10 11 return Array.isArray(obj) && obj.every(isProduct) 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 return products const loadProducts = async(): Promise<Product[]> => { 1 const response = await fetch('https://api.mysite.com/products') 2 const products: unknown = await response.json() 3 4 if (!isArrayOfProducts(products)) { 5 throw new TypeError('Received malformed products API response') 6 } 7 8 } 9 10 const isArrayOfProducts = (obj: unknown): obj is Product[] => { 11 return Array.isArray(obj) && obj.every(isProduct) 12 } 13 14 const isProduct = (obj: unknown): obj is Product => { 15 return obj != null && typeof (obj as Product).id === 'string' 16 } 17 const loadProducts = async(): Promise<Product[]> => { const response = await fetch('https://api.mysite.com/products') const products: unknown = await response.json() if (!isArrayOfProducts(products)) { throw new TypeError('Received malformed products API response') } return products } const isArrayOfProducts = (obj: unknown): obj is Product[] => { return Array.isArray(obj) && obj.every(isProduct) } const isProduct = (obj: unknown): obj is Product => { return obj != null && typeof (obj as Product).id === 'string' } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Typescript の ユーザー定義型ガード
  14. With Aspida import aspida from '@aspida/axios' import api from '../api/$api'

    const client = api(aspida(fetch, { baseURL: 'https://api.mysite.com' })) const loadProducts = async(): Promise<Product[]> => { const res = await client.products.$get() // res: { products: Product[]} return res } 1 2 3 4 5 6 7 8 9 10 11 12 13 export type Methods = { get: { resBody: { products: Product[] } } } 1 2 3 4 5 6 7 api/products/index.ts
  15. With Aspida import aspida from '@aspida/axios' import api from '../api/$api'

    const client = api(aspida(fetch, { baseURL: 'https://api.mysite.com' })) const loadProducts = async(): Promise<Product[]> => { const res = await client.products.$get() // res: { products: Product[]} return res } 1 2 3 4 5 6 7 8 9 10 11 12 13 const client = api(aspida(fetch, { baseURL: 'https://api.mysite.com' })) import aspida from '@aspida/axios' 1 import api from '../api/$api' 2 3 4 5 6 const loadProducts = async(): Promise<Product[]> => { 7 const res = await client.products.$get() 8 // res: { products: Product[]} 9 10 return res 11 } 12 13 export type Methods = { get: { resBody: { products: Product[] } } } 1 2 3 4 5 6 7 api/products/index.ts
  16. With Aspida import aspida from '@aspida/axios' import api from '../api/$api'

    const client = api(aspida(fetch, { baseURL: 'https://api.mysite.com' })) const loadProducts = async(): Promise<Product[]> => { const res = await client.products.$get() // res: { products: Product[]} return res } 1 2 3 4 5 6 7 8 9 10 11 12 13 const client = api(aspida(fetch, { baseURL: 'https://api.mysite.com' })) import aspida from '@aspida/axios' 1 import api from '../api/$api' 2 3 4 5 6 const loadProducts = async(): Promise<Product[]> => { 7 const res = await client.products.$get() 8 // res: { products: Product[]} 9 10 return res 11 } 12 13 const loadProducts = async(): Promise<Product[]> => { const res = await client.products.$get() // res: { products: Product[]} return res } import api from '../api/$api' 2 3 const client = api(aspida(fetch, { baseURL: 'https://api.mysite.com' })) 4 5 6 7 8 9 10 11 12 13 14 export type Methods = { get: { resBody: { products: Product[] } } } 1 2 3 4 5 6 7 api/products/index.ts
  17. 技術選定 HTTP client: axios + aspida CSS frameWork: tailwind test:

    jest, Cypress, Mock Service Worker Next.js + Typescript Data fetching: useSWR new! new!
  18. < 600s > 600s Client Server Cache Client Server Cache

    適切なMax-age の設定が難しい・・・😓
  19. < 600s < 700s Client Server Cache Client Server Cache

    ① ② ①: stale ( 新鮮でない) ②: revalidate: ( 再検証) ③: cache 更新 ③
  20. import useSWR from "swr" export const useStaffNumber = (initialData: string)

    => { const { data: staffNumber, mutate: setStaffNumber } = useSWR( "staffNumber", null, { initialData } ) return [staffNumber as string, setStaffNumber] as const } 1 2 3 4 5 6 7 8 9 10 11 12 実戦例 import { useStaffNumber } from "../hooks/useSWR" const StaffNumber = () => { const [staffNumber, setStaffNumber] = useStaffNumber("") const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => setStaffNumber(event.currentTarget.value) return ( <input type="text" placeholder="7 桁の⽒名コードを⼊⼒してください" value={staffNumber} onChange={handleChange} /> ) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
  21. import useSWR from "swr" export const useStaffNumber = (initialData: string)

    => { const { data: staffNumber, mutate: setStaffNumber } = useSWR( "staffNumber", null, { initialData } ) return [staffNumber as string, setStaffNumber] as const } 1 2 3 4 5 6 7 8 9 10 11 12 "staffNumber", import useSWR from "swr" 1 2 export const useStaffNumber = (initialData: string) => { 3 const { data: staffNumber, mutate: setStaffNumber } = useSWR( 4 5 null, 6 { 7 initialData 8 } 9 ) 10 return [staffNumber as string, setStaffNumber] as const 11 } 12 実戦例 import { useStaffNumber } from "../hooks/useSWR" const StaffNumber = () => { const [staffNumber, setStaffNumber] = useStaffNumber("") const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => setStaffNumber(event.currentTarget.value) return ( <input type="text" placeholder="7 桁の⽒名コードを⼊⼒してください" value={staffNumber} onChange={handleChange} /> ) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
  22. import useSWR from "swr" export const useStaffNumber = (initialData: string)

    => { const { data: staffNumber, mutate: setStaffNumber } = useSWR( "staffNumber", null, { initialData } ) return [staffNumber as string, setStaffNumber] as const } 1 2 3 4 5 6 7 8 9 10 11 12 "staffNumber", import useSWR from "swr" 1 2 export const useStaffNumber = (initialData: string) => { 3 const { data: staffNumber, mutate: setStaffNumber } = useSWR( 4 5 null, 6 { 7 initialData 8 } 9 ) 10 return [staffNumber as string, setStaffNumber] as const 11 } 12 null, import useSWR from "swr" 1 2 export const useStaffNumber = (initialData: string) => { 3 const { data: staffNumber, mutate: setStaffNumber } = useSWR( 4 "staffNumber", 5 6 { 7 initialData 8 } 9 ) 10 return [staffNumber as string, setStaffNumber] as const 11 } 12 実戦例 import { useStaffNumber } from "../hooks/useSWR" const StaffNumber = () => { const [staffNumber, setStaffNumber] = useStaffNumber("") const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => setStaffNumber(event.currentTarget.value) return ( <input type="text" placeholder="7 桁の⽒名コードを⼊⼒してください" value={staffNumber} onChange={handleChange} /> ) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
  23. import useSWR from "swr" export const useStaffNumber = (initialData: string)

    => { const { data: staffNumber, mutate: setStaffNumber } = useSWR( "staffNumber", null, { initialData } ) return [staffNumber as string, setStaffNumber] as const } 1 2 3 4 5 6 7 8 9 10 11 12 "staffNumber", import useSWR from "swr" 1 2 export const useStaffNumber = (initialData: string) => { 3 const { data: staffNumber, mutate: setStaffNumber } = useSWR( 4 5 null, 6 { 7 initialData 8 } 9 ) 10 return [staffNumber as string, setStaffNumber] as const 11 } 12 null, import useSWR from "swr" 1 2 export const useStaffNumber = (initialData: string) => { 3 const { data: staffNumber, mutate: setStaffNumber } = useSWR( 4 "staffNumber", 5 6 { 7 initialData 8 } 9 ) 10 return [staffNumber as string, setStaffNumber] as const 11 } 12 import useSWR from "swr" export const useStaffNumber = (initialData: string) => { const { data: staffNumber, mutate: setStaffNumber } = useSWR( "staffNumber", null, { initialData } ) return [staffNumber as string, setStaffNumber] as const } 1 2 3 4 5 6 7 8 9 10 11 12 実戦例 import { useStaffNumber } from "../hooks/useSWR" const StaffNumber = () => { const [staffNumber, setStaffNumber] = useStaffNumber("") const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => setStaffNumber(event.currentTarget.value) return ( <input type="text" placeholder="7 桁の⽒名コードを⼊⼒してください" value={staffNumber} onChange={handleChange} /> ) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
  24. import useSWR from "swr" export const useStaffNumber = (initialData: string)

    => { const { data: staffNumber, mutate: setStaffNumber } = useSWR( "staffNumber", null, { initialData } ) return [staffNumber as string, setStaffNumber] as const } 1 2 3 4 5 6 7 8 9 10 11 12 "staffNumber", import useSWR from "swr" 1 2 export const useStaffNumber = (initialData: string) => { 3 const { data: staffNumber, mutate: setStaffNumber } = useSWR( 4 5 null, 6 { 7 initialData 8 } 9 ) 10 return [staffNumber as string, setStaffNumber] as const 11 } 12 null, import useSWR from "swr" 1 2 export const useStaffNumber = (initialData: string) => { 3 const { data: staffNumber, mutate: setStaffNumber } = useSWR( 4 "staffNumber", 5 6 { 7 initialData 8 } 9 ) 10 return [staffNumber as string, setStaffNumber] as const 11 } 12 import useSWR from "swr" export const useStaffNumber = (initialData: string) => { const { data: staffNumber, mutate: setStaffNumber } = useSWR( "staffNumber", null, { initialData } ) return [staffNumber as string, setStaffNumber] as const } 1 2 3 4 5 6 7 8 9 10 11 12 実戦例 import { useStaffNumber } from "../hooks/useSWR" const StaffNumber = () => { const [staffNumber, setStaffNumber] = useStaffNumber("") const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => setStaffNumber(event.currentTarget.value) return ( <input type="text" placeholder="7 桁の⽒名コードを⼊⼒してください" value={staffNumber} onChange={handleChange} /> ) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const [staffNumber, setStaffNumber] = useStaffNumber("") import { useStaffNumber } from "../hooks/useSWR" 1 2 const StaffNumber = () => { 3 4 5 const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => 6 setStaffNumber(event.currentTarget.value) 7 8 return ( 9 <input 10 type="text" 11 placeholder="7 桁の⽒名コードを⼊⼒してください" 12 value={staffNumber} 13 onChange={handleChange} 14 /> 15 ) 16 } 17
  25. import useSWR from "swr" export const useStaffNumber = (initialData: string)

    => { const { data: staffNumber, mutate: setStaffNumber } = useSWR( "staffNumber", null, { initialData } ) return [staffNumber as string, setStaffNumber] as const } 1 2 3 4 5 6 7 8 9 10 11 12 "staffNumber", import useSWR from "swr" 1 2 export const useStaffNumber = (initialData: string) => { 3 const { data: staffNumber, mutate: setStaffNumber } = useSWR( 4 5 null, 6 { 7 initialData 8 } 9 ) 10 return [staffNumber as string, setStaffNumber] as const 11 } 12 null, import useSWR from "swr" 1 2 export const useStaffNumber = (initialData: string) => { 3 const { data: staffNumber, mutate: setStaffNumber } = useSWR( 4 "staffNumber", 5 6 { 7 initialData 8 } 9 ) 10 return [staffNumber as string, setStaffNumber] as const 11 } 12 import useSWR from "swr" export const useStaffNumber = (initialData: string) => { const { data: staffNumber, mutate: setStaffNumber } = useSWR( "staffNumber", null, { initialData } ) return [staffNumber as string, setStaffNumber] as const } 1 2 3 4 5 6 7 8 9 10 11 12 実戦例 import { useStaffNumber } from "../hooks/useSWR" const StaffNumber = () => { const [staffNumber, setStaffNumber] = useStaffNumber("") const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => setStaffNumber(event.currentTarget.value) return ( <input type="text" placeholder="7 桁の⽒名コードを⼊⼒してください" value={staffNumber} onChange={handleChange} /> ) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const [staffNumber, setStaffNumber] = useStaffNumber("") import { useStaffNumber } from "../hooks/useSWR" 1 2 const StaffNumber = () => { 3 4 5 const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => 6 setStaffNumber(event.currentTarget.value) 7 8 return ( 9 <input 10 type="text" 11 placeholder="7 桁の⽒名コードを⼊⼒してください" 12 value={staffNumber} 13 onChange={handleChange} 14 /> 15 ) 16 } 17 const [staffNumber, setStaffNumber] = useStaffNumber("") const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => setStaffNumber(event.currentTarget.value) import { useStaffNumber } from "../hooks/useSWR" 1 2 const StaffNumber = () => { 3 4 5 6 7 8 return ( 9 <input 10 type="text" 11 placeholder="7 桁の⽒名コードを⼊⼒してください" 12 value={staffNumber} 13 onChange={handleChange} 14 /> 15 ) 16 } 17
  26. import useSWR from "swr" export const useStaffNumber = (initialData: string)

    => { const { data: staffNumber, mutate: setStaffNumber } = useSWR( "staffNumber", null, { initialData } ) return [staffNumber as string, setStaffNumber] as const } 1 2 3 4 5 6 7 8 9 10 11 12 "staffNumber", import useSWR from "swr" 1 2 export const useStaffNumber = (initialData: string) => { 3 const { data: staffNumber, mutate: setStaffNumber } = useSWR( 4 5 null, 6 { 7 initialData 8 } 9 ) 10 return [staffNumber as string, setStaffNumber] as const 11 } 12 null, import useSWR from "swr" 1 2 export const useStaffNumber = (initialData: string) => { 3 const { data: staffNumber, mutate: setStaffNumber } = useSWR( 4 "staffNumber", 5 6 { 7 initialData 8 } 9 ) 10 return [staffNumber as string, setStaffNumber] as const 11 } 12 import useSWR from "swr" export const useStaffNumber = (initialData: string) => { const { data: staffNumber, mutate: setStaffNumber } = useSWR( "staffNumber", null, { initialData } ) return [staffNumber as string, setStaffNumber] as const } 1 2 3 4 5 6 7 8 9 10 11 12 実戦例 import { useStaffNumber } from "../hooks/useSWR" const StaffNumber = () => { const [staffNumber, setStaffNumber] = useStaffNumber("") const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => setStaffNumber(event.currentTarget.value) return ( <input type="text" placeholder="7 桁の⽒名コードを⼊⼒してください" value={staffNumber} onChange={handleChange} /> ) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const [staffNumber, setStaffNumber] = useStaffNumber("") import { useStaffNumber } from "../hooks/useSWR" 1 2 const StaffNumber = () => { 3 4 5 const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => 6 setStaffNumber(event.currentTarget.value) 7 8 return ( 9 <input 10 type="text" 11 placeholder="7 桁の⽒名コードを⼊⼒してください" 12 value={staffNumber} 13 onChange={handleChange} 14 /> 15 ) 16 } 17 const [staffNumber, setStaffNumber] = useStaffNumber("") const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => setStaffNumber(event.currentTarget.value) import { useStaffNumber } from "../hooks/useSWR" 1 2 const StaffNumber = () => { 3 4 5 6 7 8 return ( 9 <input 10 type="text" 11 placeholder="7 桁の⽒名コードを⼊⼒してください" 12 value={staffNumber} 13 onChange={handleChange} 14 /> 15 ) 16 } 17 import { useStaffNumber } from "../hooks/useSWR" const StaffNumber = () => { const [staffNumber, setStaffNumber] = useStaffNumber("") const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => setStaffNumber(event.currentTarget.value) return ( <input type="text" placeholder="7 桁の⽒名コードを⼊⼒してください" value={staffNumber} onChange={handleChange} /> ) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
  27. useContext と⽐較した感想 参考記事: Shared Hook State with SWR SWR を

    状態管理 として活⽤しているよという話