Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rerendering component and useReducer initialization

EDIT: below the line is the initial problem where I asked if my whole architecture was fine. I later edited the topic (and title) to go straight to what was my issue in order to help the community. Therefore, you might find what you want by jumping directly to the answer while skipping the main post


I am new to react and I am encountering issues back to back. I suspect that something is wrong in my pattern and would really love to have it criticized by someone a bit stronger.

Component A:

It renders (among others) a Component B

Component A has a useState hook on an array of C_obj it renders, called C_obj_arr

const [C_obj_arr, ASetter] = useState<C_obj[]>()

It provides this C_obj_arr and ASetter to Component B as properties to allow the data to go back up.

Component B
It renders each C_obj of the C_obj_arr in a list of Component C.

It has a useReducer hook that controls the states of C_obj_arr

const [C_obj_array, dispatch] = useReducer(reducer, props.C_obj_array);

It has a useEffect hook such that if C_obj_arr changes, data goes back up to Compoennt A

useEffect(() => {
      ASetter(C_obj_array);
  }, [C_obj_array, ASetter]); 

Question: Is it fine so far to use the prop like this to init the reducer?

it also uses a useCallback hook to wrap a function that will allow getting the data back from Component C

  const fn_callback = useCallback(
    (c: C_obj) =>
      dispatch({
        kind: Kind.AN_ACTION,
        payload: { wells_plan: c },
      }),
    []
  );

Component C

It has another useReducer that controls the states of C_obj

const [C_obj, dispatch] = useReducer(reducer, props.C_obj);

To send the information back up to Component B, it uses the function fn_callback, created in B thanks to a useEffect hook with dep on C_obj

  useEffect(() => {
    props.fn_callback(C_obj);
  }, [C_obj, props.fn_callback]);

I hope it is not a total brain schmuck to read, I am very new to all of that so I understand I can be doing something totally broken by design. Many thanks for help

EDIT: as requested, here is a block of code to synthetize


const A = (): JSX.Element => {
  const [C_obj_arr, ASetter] = useState<C_obj[]>();

  return (
    <>
      <B>C_obj_arr=C_obj_arr ASetter=ASetter</B>
    </>
  );
};

const B = (C_obj_arr_init: C_obj[], ASetter: () => void): JSX.Element => {
  const [C_obj_array, dispatch] = useReducer(reducer, C_obj_arr_init);

  useEffect(() => {
    ASetter(C_obj_array);
  }, [C_obj_array, ASetter]);

  const fn_callback = useCallback(
    (c_obj: C_obj) =>
      dispatch({
        kind: Kind.UPDATE_OBJ,
        payload: { wells_plan: c_obj },
      }),
    []
  );

  return C_obj_array.map(C_obj => (
    <C C_obj={C_obj} fn_callback={fn_callback}></C>
  ));
};

const C = (C_obj_init, fn_callback): JSX.Element => {
  const [C_obj, dispatch] = useReducer(reducer, C_obj_init);

  useEffect(() => {
    fn_callback(C_obj);
  }, [C_obj, fn_callback]);

  return <div>{C.toString()}</div>;
};

like image 519
zakrapovic Avatar asked Mar 22 '26 08:03

zakrapovic


1 Answers

I assume, that you mean

import { useState, useEffect, useReducer, useCallback } from "react"

type SomeObj = {
  name: string
  key: string
}

const A = (): JSX.Element => {
  const [items, setitems] = useState<SomeObj[]>([
    {
      key: "a",
      name: "hello"
    }
  ])

  return (
    <>
      <B items={items} setitems={setitems} />
    </>
  )
}

const B = ({ items, setitems }: { items: SomeObj[]; setitems: (x: SomeObj[]) => void }): JSX.Element => {
  const [items2, dispatch] = useReducer(
    (
      x: SomeObj[],
      a: {
        kind: string
        payload: {}
      }
    ) => {
      return x
    },
    items
  )

  useEffect(() => {
    setitems(items2)
  }, [items2, setitems])

  const fn_callback = useCallback(
    (item: SomeObj) =>
      dispatch({
        kind: "update",
        payload: { wells_plan: item },
      }),
    []
  )

  return (
    <div>
      {items2.map((item) => (
        <C itemInit={item} fn_callback={fn_callback} key={item.key}></C>
      ))}
    </div>
  )
}

const C = ({ itemInit, fn_callback }: { itemInit: SomeObj; fn_callback: (c_obj: SomeObj) => void }): JSX.Element => {
  const [item, dispatch] = useReducer((x: SomeObj) => x, itemInit)

  useEffect(() => {
    fn_callback(item)
  }, [item, fn_callback])

  return <div>{item.name}</div>
}

function App() {
  return (
    <main>
      <A></A>
    </main>
  )
}

export default App

And, I think, it's basically the reason for using global/atom state managers to react like: redux, recoil, jotai, etc.

In your scenario, you have a couple of prop passing layers through components, and only if I understand correctly, you try to take up changes from deeply nested component.

Using global state, you can have only one array and source of truth, and only one handler in each child component. That probably will remove all unnecessary hooks

like image 188
Фарид Ахмедов Avatar answered Mar 24 '26 22:03

Фарид Ахмедов



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!