🥳 welcome interested partners, do something meaningful together!

I launched a weekly translation project at github.com and fedarling.github. IO

Currently, there is still a lack of like-minded partners, which is purely personal interest. Of course, it will also help to improve English and front-end skills. Requirements: English should not be too bad, proficient in using Github, persistent, modest, and responsible for what they do.

If you want to participate, you can either click here to view it or send an issue message to the warehouse. My blog also has specific personal contact information: daodaolee.cn

preface

Pomodone: A small time-tracking app based on the Pomodoro technique of working in 25-minute intervals. It has a 25-minute timer (running in the Web Worker) and saves the history of the “poms” to a small Firebase database, the portal

Usage of both

The React hook is a good wrapper. I created a useCurrentUser hook that listens for authentication changes and sets some states accordingly. Then, when I notice authentication changes, I can be sure React will re-render as needed.

export const useCurrentUser = () => {
  const [currentUser, setCurrentUser] = useState(undefined)

  useEffect(() => {
    return firebase.auth().onAuthStateChanged((details) => {
      setCurrentUser(
        details
          ? {
              displayName: details.displayName,
              provider: {
                'google.com': 'Google',
                'github.com': 'GitHub',
              }[details.providerData[0].providerId],
              uid: details.uid,
            }
          : null
      )
    })
  }, [])
  return [currentUser]
}
Copy the code

In any component, you can write:

const [currentUser] = useCurrentUser()
Copy the code

This way of writing is minimal and allows any component to quickly access the current user. The only downside is that there can be many onAuthStateChanged listeners, which can be avoided by binding only one listener or putting the current user in the current context.

When it comes to context, it’s more like writing in Svelte: Writable Store

export const currentUser = writable()

export const listenForAuthChanges = () = > {
  return firebase.auth().onAuthStateChanged((details) = > {
    if (details) {
      currentUser.set({
        displayName: details.displayName,
        provider: {
          'google.com': 'Google'.'github.com': 'GitHub',
        }[details.providerData[0].providerId],
        uid: details.uid,
      })
    } else {
      currentUser.set(null)}}}Copy the code

In advanced Svelte components, you can call it from onMount, which runs once when the component is installed (this method is returned, so you can unsubscribe when removing the component, just as useEffect returns a function).

onMount(() = > {
  return listenForAuthChanges()
})
Copy the code

Now, the component can import the currentUser writable Store anywhere in the Svelte code. What I care about is that currentUser is not a value, but a store, and therefore has absolute state control. Of course, you can subscribe to it and manually modify the status changes:

currentUser.subscribe(newValue= >{... })Copy the code

Or, if you just want to read the latest value, you can prefix it with $:

console.log($currentUser)
Copy the code

These are some syntax tricks for Svelte. The $sign automatically subscribes to the latest value in the store. The advantage is that Svelte doesn’t have to subscribe every time it needs to read the latest value.

In short, both libraries seem to take a similar approach: both allow you to subscribe to a Firebase listener (demo above) and update the data when the state changes.

The use of the Worker

The Pomodone I designed had to keep the 25-minute timer running as accurately as possible. If browser tabs are in the background (for example, not the focus TAB), most browsers will lower the priority of their setTimeout calls rather than running them strictly by time. Most of the time on the web this is not a big deal, but when users track 25 minutes of work through your application, it does! In addition, any slight time difference over the course of 25 minutes can cause the final time to be far apart. However, if these timeouts are moved to the Web Worker, they run on time and are not de-prioritized by the browser.

Therefore, in my Tracker component, I need to instantiate a Web worker, send information and receive data (such as remaining time). I find React more cumbersome to manage than Svelte. Since the React component is re-executed every time the component is re-rendered, it is easy to create thousands of workers. In this case, you need to use useRef to reference the created worker to avoid this problem.

First, I set the required initial state of the component:

const [currentPom, setCurrentPom] = useState(null)
const [currentUser] = useCurrentUser()
const worker = useRef(null)
Copy the code

Then, create a useEffect hook to instantiate the worker and bind an event if necessary:

useEffect(() => { if (! worker.current) { worker.current = new Worker(workerURL) window.worker = worker.current } const onMessage = (event) => {  if (event.data.name === 'tick') { setCurrentPom((currentPom) => ({ ... currentPom, secondsRemaining: event.data.counter, })) } else if (event.data.name === 'start') { // More branches removed here to save space... } } worker.current.addEventListener('message', onMessage) return () => { worker.current.removeEventListener('message', onMessage) } }, [currentUser])Copy the code

Next, when the user clicks the Start button, a message will be sent to the worker:

const onStartPom = () => { if (! worker.current) return worker.current.postMessage('startTimer') }Copy the code

If written in Svelte, the code is similar, but it is easier in two places:

  1. You don’t need to keep the worker on the userRef, just assign a variable
  2. Event listeners can be wrapped in functions that are not dependent on userEffect — that is, wrapped in userCallback.
let worker
onMount(() = > {
  worker = new Worker(workerURL)
  worker.addEventListener('message', onWorkerMessage)
  return () = > {
    worker.removeEventListener('message', onWorkerMessage)
  }
})
Copy the code

There is no need to use the React setX(oldX => newX) constraint to set the state, just change the local variable:

function onWorkerMessage(event) {
  if (event.data.name === 'tick') { currentPom = { ... currentPom,secondsRemaining: event.data.counter,
    }
  } else if (event.data.name === 'start') {
    // More branches here removed to save space...}}Copy the code

Conditions apply colours to a drawing

Part of what I like about React is embedding JS in template syntax:

// React
<ul>
  {pomsForCurrentDay.map(entryData, index) => {
    const finishedAt = format(new Date(entryData.timeFinished), 'H:mm:ss')
    return <li title={`Finished at ${finishedAt}`}>{index + 1}</li>
  })}
</ul>
Copy the code

But Svelte’s template syntax is also very useful:

// Svelte
<ul class="poms-list">
  {#each currentDayPoms as value, index}
    <li
      title={`Finished at ${format(
        new Date(value.timeFinished),
        'H:mm:ss'
      )}`}
    >
      {index + 1}
    </li>
  {/each}
</ul>
Copy the code

Svelte’s syntax for rendering components is also very simple, much like JSX’s:

// Svelte
<History pomodoros={pomodoros} />
  
// Svelte(collapsed props)
<History {pomodoros} / >
Copy the code

Reactive $in Svelte

React requires the use of userEffect and other hooks to control the code to run and re-run the code when the component is re-rendered. Svelte is different in that, by default, most of the code runs only once. Of course, React re-rendering also has its advantages when a node rendering inclusion function is running with a lot of data. Like this:

const input = props.inputData
const transformed = input.map((item) => transformItem(item))

return <div>{JSON.stringify(transformed, null, 2)}</div>
Copy the code

If the user provides new props. InputData, the component will be re-rendered and updated, and the value will be new. This is not the case in Svelte:

<script>
export let input;
const transformed = input.map((item) = > transformItem(item))
</script>

<div>{JSON.stringify(transformed, null, 2)}</div>
Copy the code

To solve reactive problems, either use the $sign or add conversion logic to the template:

/ / use the $<script>
export let input;
$: transformed = input.map((item) = > transformItem(item))
</script>

<div>{JSON.stringify(transformed, null, 2)}</div>// Use templates<script>
export let input;
</script>

<div>{JSON.stringify(input.map((item => transformItem(item))), null, 2)}</div>
Copy the code

Modular assembly

Composite components are particularly important in component-level frameworks, and React’s children property simplifies rendering:

function Box(props) { return <div>{props.children}</div> } function App() { return ( <Box> <p>hello world! </p> </Box> ) }Copy the code

Svelte with slots:

<! -- Box component -->
<div class="box">
  <slot></slot>
</div>

<! -- App component -->
<Box>
  <p>hello world!</p>
</Box>
Copy the code

When multiple child components are involved, they are handled differently:

  • React will have multiple Props:

    function Box(props) { return ( <div> <div class="left">{props.left}</div> <div class="right">{props.right}</div> </div> ) } function App() { return <Box left={<p>hello</p>} right={<p>world! </P>} /> }Copy the code
  • Naming multiple slots will be displayed:

    <! -- Box component -->
    <div class="box">
      <slot name="left"></slot>
      <slot name="right"></slot>
    </div>
    
    <! -- App component -->
    <Box>
      <p slot="left">hello</p>
      <p slot="right">world!</p>
    </Box>
    Copy the code

The name of the class to determine

Svelte has a small function that conditionally assigns a class:

<div class:is-active={isActive}>
Copy the code

Event listeners

Svelte contains some modifiers for binding events:

<script>
function click() {
  // No need to preventDefault ourselves
  // logic here
}
</script>

<button on:click|preventDefault={click}>
  Click me!
</button>
Copy the code

A link to the

The original link

Translation plan