פיתוח React עם TypeScript
פוסט זה כולל טיפ קצר בנושא פיתוח Front End. אם אתם רוצים ללמוד יותר לעומק על פיתוח Front End מהבסיס ועד הנושאים המתקדמים תשמחו לשמוע שבניתי קורס וידאו מקיף בנושא זה הכולל מעל 50 שיעורי וידאו והמון תרגול מעשי.
למידע נוסף והצטרפות לקורס בקרו בדף קורס Front End באתר.
אחרי שפייסבוק ירדו מ Flow ועברו ל TypeScript חשבתי שכדאי לנסות גם - אז לקחתי את משחק תפוס ת'אדום וכתבתי אותו עם React ו TypeScript. הנה המסקנות וגם מדריך קצר איך להתחיל פרויקט React ו TypeScript.
1. מה אנחנו בונים
משחק תפוס ת'אדום הוא משחקון ממש פשוט שנוכל לכתוב בתור קומפוננטת ריאקט יחידה. לוח המשחק כולל מספר ריבועים כאשר אחד מהם אדום והשאר אפורים. לחיצה על האדום נותנת 10 נקודות, לחיצה על האפורים לוקחת 5 נקודות וכל לחיצה מערבבת מחדש את מיקומי הריבועים.
מימוש המשחק בתור פקד ריאקט פשוט יראה כך:
import React from 'react';
import _ from 'lodash';
export default class GameComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
winnerIndex: _.random(props.size - 1),
score: 0,
}
}
onClick(idx: number) {
let score = this.state.score;
if (idx === this.state.winnerIndex) {
score += 10;
} else {
score -= 5;
}
this.setState(oldState => ({
winnerIndex: _.random(this.props.size - 1),
score,
}))
}
render() {
const { size } = this.props;
const { winnerIndex, score } = this.state;
return (
<div>
<p>Score: {score}</p>
<div className='container'>
{
_.range(size).map(idx => (
<div
className={`square ${idx === winnerIndex ? 'winner' : ''}`}
onClick={(_ev) => this.onClick(idx)}
>
</div>
))
}
</div>
</div>
)
}
}
בואו נהפוך את זה ל TypeScript כדי שה IDE יוכל לזהות טעויות בשבילנו. המעבר ל TypeScript כולל הגדרת הטיפוסים של ה Props וה State של הפקד, והוספת הגדרת טיפוסים בתוך הפונקציות.
ב TypeScript יש טיפוס שנקרא Interface שמאפשר להגדיר אוסף של שדות. אנחנו נשתמש בו כדי להגדיר את השדות וסוגיהם גם ל props וגם ל state:
interface IProps {
size: number
}
interface IState {
winnerIndex: number,
score: number,
}
ה winnerIndex וה score הם ערכים שמשתנים בכל סיבוב ולכן שייכים ל state; ה size נשאר קבוע לאורך חיי הפקד ומתקבל מבחוץ ולכן מגיע בתור props. כדי להשתמש בהגדרות אלה עלינו להוסיף אותן לשורת הגדרת הפקד וגם לבנאי:
export default class GameComponent extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
winnerIndex: _.random(props.size - 1),
score: 0,
}
}
// ...
}
השינוי הבא הוא בהגדרת הפונקציה onClick - הפעם אנחנו יודעים שהפונקציה מצפה לקבל מספר אז נוכל לספר את זה גם ל TypeScript:
onClick(idx: number) {
let score = this.state.score;
if (idx === this.state.winnerIndex) {
score += 10;
} else {
score -= 5;
}
this.setState(oldState => ({
winnerIndex: _.random(this.props.size - 1),
score,
}))
}
כדי לתגמל אותנו על ההשקעה TypeScript יוכל עכשיו לזהות כשנעביר ערך מסוג לא נכון ל onClick, או כשננסה למשוך מידע משדות לא נכונים ב props ו state. השורה הבאה למשל תציג שגיאה כבר בתוך סביבת העריכה כי אין ב props שדה בשם score (השדה מאוחסן ב state):
render() {
const { score } = this.props;
return (
<div>
<p>Score: {score}</p>
</div>
)
}
2. הגדרות הפרויקט ופרטים טכניים אחרים
בשביל להתחיל לעבוד עם TypeScript בפרויקט שלכם תצטרכו להתקין את הסיפריות הבאות:
npm install --save react react-dom @types/react @types/react-dom typescript awesome-typescript-loader source-map-loader
לאחר מכן לעדכן את הקובץ tsconfig.json (או ליצור אחד אם זה פרויקט חדש) כך שיכיל את התוכן הבא:
{
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": true,
"module": "commonjs",
"target": "es5",
"allowJs": true,
"esModuleInterop": true,
"jsx": "react"
}
}
ולסיום לעדכן את קובץ הגדרות ה webpack שלכם כך שיכיל את התוכן הבא:
// webpack.config.js
const path = require('path');
const webpack = require('webpack');
const ROOT = path.resolve( __dirname, 'src' );
const DESTINATION = path.resolve( __dirname, 'dist' );
module.exports = {
context: ROOT,
entry: {
'main': './main.tsx'
},
output: {
filename: '[name].bundle.js',
path: DESTINATION
},
resolve: {
extensions: ['.ts', '.js', '.tsx'],
modules: [
ROOT,
'node_modules'
]
},
module: {
rules: [
/****************
* PRE-LOADERS
*****************/
{
enforce: 'pre',
test: /\.js$/,
use: 'source-map-loader'
},
{
enforce: 'pre',
test: /\.ts$/,
exclude: /node_modules/,
use: 'tslint-loader'
},
/****************
* LOADERS
*****************/
{
test: /\.tsx?$/,
exclude: [ /node_modules/ ],
use: 'awesome-typescript-loader'
}
]
},
devtool: 'cheap-module-source-map',
devServer: {}
};
אחרי זה צרו קובץ בשם main.tsx בתיקית src והוא יהיה נקודת הכניסה ליישום שלכם. תוכן הקובץ אצלי נראה כך:
// main.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import Game from './components/game';
const el = document.querySelector('#game');
ReactDOM.render(<Game size={9} />, el);
כל קובץ שמסתיים ב tsx ייחשב לקובץ TypeScript JSX ויומר על ידי Babel וגם יוצג בצורה יפה בעורך הטקסט Visual Studio Code, כולל כל ההשלמות האוטומטיות והפינוקים.
העליתי לגיטהאב את הקוד המלא שעובד כדי שתוכלו להשתמש בו כבסיס לפרויקטים שלכם ואתם יכולים למצוא את הקוד בקישור כאן:
https://github.com/ynonp/redspotter-typescript/tree/ts-react
חוץ מזה אם הפוסט הזה עשה לכם חשק ללמוד יותר על TypeScript מוזמנים לבוא ביום חמישי בבוקר לוובינר שאני מעביר בנושא שם תוכלו לראות בשידור חי איך TypeScript מזהה טעויות בפרויקט, לשאול שאלות וללמוד יותר על השפה. הוובינר בחינם והרישום בקישור כאן: