DEV Community

Cover image for The paradigm of React
Kenneth Lum
Kenneth Lum

Posted on • Edited on

The paradigm of React

I think it may be important to know one of the top principles of React, because without it, the code may turn out a bit non-idiomatic React, and it may need refactoring to bring it back to the React Way. This one major principle is: use declarative ways to write your components.

Sometimes, it may need to be imperative, but those are supposed to change a state, so that the state can change into something you declared earlier.

For example, you may say that, when you have an array, you show the list as:

<ul>
  {arr.map((e, i) => <li key={e.id}>{e.title}</li>)}
</ul>
Enter fullscreen mode Exit fullscreen mode

So you are declaring: with the arr, what should the output be.

Now again, if you have filtering of the items, if the user can enter a keyword, then the code becomes:

const filteredArray = arr.filter(e => e.title.includes(keyword));

return (
  <ul>
    {filteredArray.map((e, i) => <li key={e.id}>{e.title}</li>)}
  </ul>
);
Enter fullscreen mode Exit fullscreen mode

You may not need to use useMemo for the filteredArray. However, imagine if you have a case where, if the user types in keyword, and needs to click on the "Filter" button to do the filtering, then you don't want to do the costly filtering for every user keypress. So if your code causes the main component to re-render every time when there is a keypress, then you may want to use the useMemo to do the filtering, depending on what keywordConfirmed is, when the user clicks on the "Filter" button. (There is a way to let a sub-component not cause the main component to re-render on each user keypress, so the filtering won't be done every time, but it is beyond the scope of this post).

There is also this idea that: if the slowest computer and smartphone can handle your task of filtering every time in a super fast way, then, you may not need to use useMemo. Optimize it only when it is necessary. Note that this is different from the LeetCode way, when n can be 5 million or 5 billion. Here, n could be 50 in your use case, or 500, so optimizing in such cases can be viewed as premature optimization. (Donald Knuth: "The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; premature optimization is the root of all evil (or at least most of it) in programming.")

Now what we have so far are all declarative: when arr is such, and keyword is such, then the output will be such: meaning it is like a function: what goes in, what goes out, and it is functional, or declarative. If you are familiar with the programming language Lisp, that's how it works.

One note about this: when you consider the states, in general, try to have as few of them as possible. Also, look at what needs to be the state, and what are values that can be derived from the state. For example, in the example above, arr is the state, and filteredArray can be derived from it. So don't make filteredArray a state, but make it a local, derived value inside this functional component.

We also use these states and write the rest of the component using them. For example, we may have

  {arr === null && <div>Loading...</div>}
Enter fullscreen mode Exit fullscreen mode

and it is again, declarative.

Now there are times we need to do imperative operations, such as to fetch the value when the component is first rendered. (it could be this component or a higher up component).

To do that, it'd be

useEffect(() => {
  fetch(url)
    .then(res => res.json())
    .then(data => setArr(data))
    .catch(console.error);
}, []);
Enter fullscreen mode Exit fullscreen mode

So here, we fetch data from the server, and set it to the arr state. And from here, everything becomes declarative again.

The same with event handlers. You'd usually write the handler to change one or more states, so that the declarative code will handle the rest.

The rule is: usually it is either the user doing something, or the network sends back some data, or setTimeout, setInterval triggers some change, and therefore changes the state. From that point on, the declarative mechanism handles the rest.

In other words, it is events: user interactions, timer events, network events, that change the states, and then from there, everything is handled in a declarative way. That is: you don't have to do anything.

This concept is important, because if you use React, and you write it without thinking mostly declarative, and use both declarative and imperative ways, then your React code will not idiomatic and it may be against the overall paradigm that React is using.

Example:
https://codesandbox.io/s/funny-hugle-94j2cj?file=/src/App.js

Reference: https://react.dev/learn/managing-state
Note that there are seven sections for this topic.

Top comments (0)