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

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

Avatar for hiroya iizuka hiroya iizuka
July 30, 2021
430

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

Next.js 開発の話です。

Avatar for hiroya iizuka

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 を

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