question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Object as an atom will cause re-render even when use selector?

See original GitHub issue
import * as React from "react";
import { atom, useRecoilValue, useSetRecoilState, selector } from "recoil";
import "./App.css";

const valueState = atom({
  key: "value",
  default: { a: 1, b: 2 },
});

const selectA = selector({
  key: "selectA",
  get: ({ get }) => get(valueState).a,
});

function ValueChild() {
  const { a } = useRecoilValue(valueState);

  return <div>useRecoilValue a:{a}</div>;
}

function SelectorChild() {
  const a = useRecoilValue(selectA);

  return <div>useRecoilValue + Selector a:{a}</div>;
}

function App() {
  const setValues = useSetRecoilState(valueState);
  const onlyChangeB = () => {
    setValues((v) => ({ ...v, b: 3 }));
  };

  return (
    <div className="App">
      <ValueChild />
      <SelectorChild />
      <button onClick={onlyChangeB}>update</button>
    </div>
  );
}

export default App;

Bootstrapped a new create-react-app and play with it.

I declared an atom as an object { a: 1, b: 2 }

with 2 components which consume it:

  • <ValueChild> only subscribe to property a
  • <SelectorChild> only subscribe to property a with selector

Expect: change property b should not trigger re-render of components who subscribe to property a

Actual behavior when I click the button to update only b’s value, both components will get re-rendered.

Anything I am missing here? Thanks. Very nice API and idea. I love the work. Tried to replace redux/mobx with this. But this will be a blocker

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:12 (4 by maintainers)

github_iconTop GitHub Comments

6reactions
acutmorecommented, May 17, 2020

Hi @rwieruch, the reason all three update is because each itemSelector reads and updates listState. If a selector’s dependencies change then the component will be re-rendered, even if the resulting selector value does not change. To isolate component updates from each other, then the atoms and selectors all need to be isolated from each other.

Something like this should work:

const listItem = RecoilUtils.atomFamily({
  key: "ListItem",
  default: (id) => ({ id, count: 0 }),
});

const listState = Recoil.atom({
  key: "list",
  default: [{ id: uuidv4() }, { id: uuidv4() }, { id: uuidv4() }],
});

const App = () => {
  const list = Recoil.useRecoilValue(listState);

  return (
    <ul>
      {list.map((item) => (
        <Counter key={item.id} id={item.id} />
      ))}
    </ul>
  );
};

const Counter = React.memo(({ id }) => {
  const [item, setItem] = Recoil.useRecoilState(listItem(id));

  function handleChange(diff) {
    setItem((item) => ({ ...item, count: item.count + diff }));
  }

  return (
    <div>
      <button type="button" onClick={() => handleChange(1)}>
        Increase
      </button>
      <button type="button" onClick={() => handleChange(-1)}>
        Decrease
      </button>

      {item.count}
    </div>
  );
});
4reactions
drarmstrcommented, May 16, 2020

Subscriptions are based on individual atoms and selectors. Currently, all downstream subscribers of an atom or selector will update when a dependency updates. If you wish to have your component only update when a or b is updated, then they can be modeled as separate atoms.

Internally, we’re working on a mechanism to limit updates when the values have not changed. But, we’re planning to really clean up that API before publishing it.

Read more comments on GitHub >

github_iconTop Results From Across the Web

useSelector causing rerender despite using shallowEqual?
useSelector will cause a second re-render despite I've added shallowEqual . Have I understood the concept of using this shallowEqual wrongly?
Read more >
Bonus: Performance - Recoil
This component is subscribed to filteredTodoListState , so it will re-render whenever that state changes or when its parent component, TodoList , re-renders....
Read more >
Lessons learned from moving to Recoil.js - Kitemaker blog
Fortunately, it's pretty simple - if a selector returns a simple value (string, number, boolean), it triggers a re-render whenever that value ...
Read more >
Re-Render Large Lists Optimally When States Change in ...
In this article, we are going to look at how we can use Recoil's atom, selector, and hooks to display and update a...
Read more >
Hooks | React Redux
However, when an action is dispatched to the Redux store, useSelector() only forces a re-render if the selector result appears to be different...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found