• בלוג
  • מה קרה כשניסיתי לתרגם קוד מ jQuery ל React

מה קרה כשניסיתי לתרגם קוד מ jQuery ל React

27/01/2020

קיטור קטן בשביל לפתוח את השבוע והפעם על הקושי להסביר את ריאקט דרך דוגמאות פשוטות. קחו את הקוד הבא לדוגמא ב jQuery:

// Write React front end to talk to this server
async function refresh() {
    const messages = await $.get('/api/v1.0/messages');
    $('ul').html('');
    $.each(messages, function() {
        $('ul').append(`<li>${this.from} - ${this.text}</li>`);
    });
}

$('form').on('submit', async function(e) {
    e.preventDefault();
    const from = $('input[name="from"]').val();
    const text = $('input[name="text"]').val();
    const msg = { from, text };

    await $.post('/api/v1.0/messages', msg);
    $('ul').append(`<li>${from} - ${text}</li>`);
});

$('#btn-refresh').on('click', refresh);

יש פה שתי פונקציות: הראשונה מתקשרת לשרת ומבקשת רשימת הודעות ואז רושמת על המסך את כל הרשימה שקיבלה . השניה מסתכלת בטופס על שני שדות, יוצרת מהם JSON Object ושולחת אותו לשרת כדי להוסיף הודעה.

הקוד קשור כמובן לקוד ה HTML שמלווה אותו: ה HTML מגדיר את המבנה, את הטופס ואת הרשימה. הקוד גם לא ממש יעיל כיוון שכל פעם שמרעננים את רשימת ההודעות הוא מוחק את כל הרשימה ומחליף אותה ברשימה המעודכנת. אבל זה עובד.

בתרגום לריאקט הרבה מהקסם הולך לאיבוד. הנה אותו מנגנון בגירסת React ו TypeScript:

import React, {FormEvent, useState} from "react";
import $ from "jquery";

interface IMessage {
    from: string,
    text: string,
}

type EmptyFunction = () => void;

function ListMessages(props: {refresh: EmptyFunction, messages: IMessage[] }) {
    const { refresh, messages } = props;
    return (
        <>
            <button onClick={refresh}>Refresh</button>
            <ul>
                {messages.map((msg) => (
                    <li>{msg.from} - {msg.text}</li>
                ))}
            </ul>
        </>
    )

}

function NewMessage(props: {send: (from: string, text: string) => void}) {
    const [from, setFrom] = useState('');
    const [text, setText] = useState('');
    const { send } = props;

    async function handleSubmit(e: FormEvent) {
        e.preventDefault();
        await send(from, text);
        setFrom('');
        setText('');
    }

    return (
      <form onSubmit={handleSubmit}>
        <label>
          From:
          <input
              type="text"
              name="from"
              value={from}
              onChange={(e) => setFrom(e.target.value)}
          />
        </label>

        <label>
          Text:
          <input
              type="text"
              name="text"
              value={text}
              onChange={(e) => setText(e.target.value)}
          />
        </label>

        <input type="submit" value="Send" />
      </form>
    )
}

export default function MessagesPage(props: {}) {
    const [messages, setMessages] = useState<IMessage []>([]);

    async function refresh() {
        const res = await $.get('http://localhost:3000/api/v1.0/messages');
        setMessages(res);
    }

    async function send(from: string, text: string) {
        const msg = { from, text };
        await $.post('http://localhost:3000/api/v1.0/messages', msg);
        setMessages([...messages, msg]);
    }

    return (
        <>
            <NewMessage send={send} />
            <ListMessages messages={messages} refresh={refresh} />
        </>
    )
}

במקום לקבל קוד JavaScript נקי יש לנו חיבור בין קוד התצוגה לקוד הלוגיקה. ה HTML הוא ממש בתוך הפרצוף. גם החלוקה בין הקומפוננטות נראית מלאכותית כי ListMessages ו NewMessage לא באמת יכולות לעבוד לבד ותמיד חייבות להופיע בתוך MessagesPage בשביל לקבל את ה props המתאימים.

מעבר ל Redux יכול לשפר את המצב במובן זה שנצטרך להעביר פחות Properties ושיתוף רשימת ההודעות בין הקומפוננטות יהיה יותר קל, וגם את הלוגיקה נטאטא מתחת לשטיח לקבצי Redux. ועדיין מבחינת אורך הקוד נפסיד בגדול.

ריאקט יתחיל להיות משתלם רק כשהיישום יכלול הרבה יותר לוגיקה, והדרישה לביצועים טובים תהיה הרבה יותר משמעותית: כשנצטרך להציג את רשימת ההודעות לאורך מספר מסכים שונים ביישום, ובכל מסך בדרך אחרת. כשנצטרך להציג יותר קומפוננטות ויותר מידע לגבי כל הודעה, וכמובן כשנרצה לעדכן את ההודעות בזמן אמת על המסך ולשנות אותן בתגובה לאירועים שמגיעים מהשרת - בקיצור כשהסינכרון בין כל הדברים שאנחנו רואים על המסך יתחיל להיות קשה, אז נתחיל לראות את התמורה בעבור המאמץ שלנו לעבוד בריאקט (או בכל פריימוורק אחרת).