javascript - Hur lyssnar man bara på ett visst värde av ett objekt i solid-js?


const App: Component = () => {
  const [obj, setObj] = createSignal({
    name: "John",
    age: 30,
  })

  createEffect(
    on(
      () => obj().name,
      (value) => {
        console.log("name", value)
      }
    )
  )
return ()=>(<button onClick={()=> setObj(obj=> ({ ...obj, age: obj.age + 1}))}>+</button>)
}

När jag ändrar age kommer createEffect också att triggas, jag vill bara lyssna på name, liknande watch i Vue3.

function setup(){
  const obj = reactive({
    name: "John",
    age: 30,
  })
  watch(()=> obj.name,(value)=>{ console.log("name", value) })
}

Några bra idéer?

Svar #1

SolidJS kommer inte att hålla reda på individuella egenskaper hos ett signalobjekt.

Du skriver också över objektet varje gång ett värde ändras när du destrukturerar det (dvs. {...obj, }).

Om du vill att createEffect ska hålla reda på dina individuella egenskaper, prova createStore.

Här är ditt exempel skrivet med createStore istället för createSignal

import {createStore} from 'solid-js/store'; // <- This is very important

const App: Component = () => {
  const [obj, setObj] = createStore({
    name: "John",
    age: 30,
  })

  createEffect(() => console.log(obj.name));

  return (<button onClick={setObject('age', obj.age + 1)}>+</button>)
  )
}

om du kör detta exempel kommer du att se att console.log(obj.name) bara körs en gång, detta beror på att solid-js kommer att hålla reda på enskilda egenskaper när de sparas på en createStore

Svar #2

Signaler är atomära värden som tillhandahåller enkelriktad dataflöde, vilket betyder att du uppdaterar en signal genom att sätta ett nytt värde. Även om du skickar samma egenskaper kommer du att sätta ett nytt objekt, eftersom objekt jämförs efter referens i JavaScript, vilket därmed utlöser en uppdatering.

Det sagt finns det flera sätt eller lösningar för att hålla reda på en enskild egenskap hos ett objekt som lagras i en signal.

  1. Använda ett store. createStore använder en proxy internt och erbjuder sätt att spåra individuella egenskaper. Du kan använda produce, ett Immer-inspirerat API för lokaliserade mutationer och alltså uppdatera endast vissa egenskaper.

  2. Du kan skapa en memoiserad signal som spårar en individuell egenskap på ett objekt. Här körs effekten endast om åldern ändras, medan en uppdatering av namnet inte har någon effekt:

import { render } from "solid-js/web";
import { createSignal, createMemo, createEffect } from "solid-js";

function App() {
  const [person, setPerson] = createSignal({ name: "John Doe", age: 20 });
  const handleClick = () => setPerson({ name: "Jenny Doe", age: 20 });

  const age = createMemo(() => person().age);

  createEffect(() => {
    console.log("Age is updated", age());
  });

  return (
    <button type="button" onClick={handleClick}>
      {JSON.stringify(person())}
    </button>
  );
}

render(App, document.getElementById("app")!);
  1. Använd on-verktyget från solid-js:
import { render } from "solid-js/web";
import { createSignal, createEffect, on } from "solid-js";
import { createStore } from "solid-js/store";

function App() {
  const [person, setPerson] = createStore({ name: 'John Doe', age: 30 });
  const increment = () => setPerson(p => ({ ...p, age: p.age + 1 }));

  createEffect(on(() => person.name, () => {
    console.log('Tracking name');
  }));

  createEffect(on(() => person.age, () => {
    console.log('Tracking age');
  }));

  return (
    <button type="button" onClick={increment}>
      {person.name} {person.age}
    </button>
  );
}

render(App, document.getElementById("app")!);

Denna metod fungerar också för en signal:

import { render } from "solid-js/web";
import { createSignal, createEffect, on } from "solid-js";

function App() {
  const [person, setPerson] = createSignal({ name: 'John Doe', age: 30 });
  const increment = () => setPerson(p => ({ ...p, age: p.age + 1 }));

  createEffect(on(() => person.name, () => {
    console.log('Tracking name');
  }));

  createEffect(on(() => person().age, () => {
    console.log('Tracking age');
  }));

  return (
    <button type="button" onClick={increment}>
      {person().name} {person().age}
    </button>
  );
}

render(App, document.getElementById("app")!);
  1. Du kan använda equals-argumentet i createSignal:
import { render } from "solid-js/web";
import { createSignal, createEffect, on } from "solid-js";

function App() {
  const [person, setPerson] = createSignal(
    { name: "John Doe", age: 20 },
    { equals: (prev, next) => prev.age === next.age }
  );

  const handleClick = () => setPerson({ name: "Jenny Doe", age: 20 });

  createEffect(() => {
    console.log("Age is updated", person());
  });

  return (
    <button type="button" onClick={handleClick}>
      {JSON.stringify(person())}
    </button>
  );
}

render(App, document.getElementById("app")!);

Detta är plug-and-play-lösningar, men eftersom Solid är ganska flexibelt och ganska tillåtande kan du implementera din egen logik om du vill.

Relaterade artiklar