Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to trigger a state update without triggering it’s useEffect

Tags:

I’m using function components and hooks.

I have a hook that sends REST request to fetch a set of application data which involves multiple params (such as userId, productId etc) from an object in application state.

This hook uses a useEffect() along the lines of:

 useEffect(() => {
   fetch()
   …

}, [objectInState]); // {user: {id: ‘007’}, product: {id: 008}}

If the id of any resource change, I want to send this request again, hence the [objectInState]


The problem came up when I try to set up URL based routing.

When I set initial state based on id’s from URL, the useEffect hook triggers and sends one request to fetch common app data as expected.

The problem is, each component responsible for rendering particular resource in URL params (user component, product component etc) then sends a REST request on their own to fetch the details of it’s resource, and expands the details of resource in state to become something like

// {user: {id: ‘007’, name: ‘John Doe’, email:’’}, product: {id: 008, name: ‘unicorns’, price: ‘1$’}} 

but this changes are triggering the useEffect that is fetching the same application data again multiple times.

I do want to fetch the application data if there is an actual change in the id’s, for example

{user: {id: ‘007’, name: ‘John Doe’, email:’’}…

changes to

{user: {id: ‘008’, name: ‘John Wick’, email:’’}

But not when

{user: {id: ‘007’}… is expanded to {user: {id: ‘007’, name: ‘John Doe’, email:’’} by other components.

When I am expanding a current object in state from a component, if I can tell react to not trigger useEffect’s listening to that param with something like {silent: true} this problem can be avoided.

How can I make the useEffect() not trigger again in such cases? Or is there another way to implement such functionality?

like image 289
T J Avatar asked Jul 12 '19 14:07

T J


1 Answers

From what you described, you should separate the object into different useStates.

Use something like:

const [user, setUser] = useState({});
const [product, setProduct] = useState({});

/* one useEffect to get the user */
useEffect(() => {
  const user = fetchUser(userIdFromUrl);
  setUser(user);
}, [userIdFromUrl]);

/* one useEffect to get the product */
useEffect(() => {
  const product = fetchProduct(productIdFromUrl);
  setProduct(product);
}, [productIdFromUrl]);

You can even later put the useState and useEffect statements in an custom hook at will.

NOTE fetch calls are simplified for example purposes

like image 128
João Cunha Avatar answered Oct 12 '22 22:10

João Cunha