最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to listen to only a certain value of an object in solid-js? - Stack Overflow

programmeradmin7浏览0评论
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>)
}

When I change age, createEffect will also be triggered, I just want to listen on name, similar to watch in Vue3.

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

Any good ideas?

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>)
}

When I change age, createEffect will also be triggered, I just want to listen on name, similar to watch in Vue3.

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

Any good ideas?

Share Improve this question edited Aug 26, 2022 at 7:20 clencat asked Aug 22, 2022 at 3:56 clencatclencat 1595 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 6

SolidJS will not keep track of individual properties of a signal object.

Also you are overwriting the object every time a value changes when destructuring it (aka. {...obj, }).

If you want createEffect to keep track of your individual properties try createStore.

Here's your example written with createStore instead of 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>)
  )
}

if you run this example, you'll see that console.log(obj.name) will only run once, this is because solid-js will keep track of individual properties when saving them on a createStore

Signals are atomic values providing unidirectional data flow, meaning you update a signal by setting a new value. Even if you pass the same properties, you will be setting a new object, because objects are pared by reference in JavaScript, hence triggering an update.

That being said, there are multiple ways or workarounds to keep track of an individual property of an object stored in signal.

  1. Using a store. createStore uses a proxy internally and provides ways to track individual properties. You can use produce, an Immer inspried API for localized mutations hence update certain properties only.

  2. You can create a memoized signal that tracks an individual property on an object. Here only changing the age re-runs the effect however updating the name has no effect:

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. Use on utility from 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")!);

This method also works for a 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. You can use equals argument in 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")!);

These are out of the box solution but since Solid is pretty flexible and quite permissive you can implement your own logic if you like.

发布评论

评论列表(0)

  1. 暂无评论