score:1

i ended up doing similar as @tmhao2005 mentioned. my final solution looks like the following. i wanted to do this to be able to decouple table logic (selection, deletion, etc..) from presentation logic. so that i can create a generic-table'ish component (not showcased in the example below)

const rootelement = document.getelementbyid("root");
const {
  table,
  tablebody,
  tablecontainer,
  tablehead,
  tablecell,
  tablerow,
  link
} = materialui;

// to mimic async loading of data
function usedata() {
  const [data, setdata] = react.usestate([]);

  react.useeffect(() => {
    settimeout(() => setdata([1, 2, 3]), 0);
  }, []);

  return {
    data,
    deletebyindex: (idx) => {
      // some random function to mutate the state
      setdata(data.filter((_, index) => idx !== index));
    }
  };
}

function reducer(state, action) {
  switch (action.type) {
    case "init":
      return { ...state, items: action.items };
    default:
      throw new error("unknown action");
  }
}

function usetable(initialitems) {
  const isfunctioncreator = react.usememo(
    () => typeof initialitems === "function",
    [initialitems]
  );

  const [state, dispatch] = react.usereducer(reducer, {
    items: isfunctioncreator ? null : initialitems,
    selecteditems: [],
    allselected: false
  });

  const data = isfunctioncreator ? initialitems() : null;

  react.useeffect(() => {
    if (isfunctioncreator && data) {
      dispatch({ type: "init", items: data });
    }
  }, [isfunctioncreator, data]);

  return {
    ...state
  };
}


const app = () => {
  const { data, deletebyindex } = usedata();
  const ctrl = usetable(() => data);

  if (!data) return <div>loading</div>;

  return (
    <tablecontainer>
      <table>
        <tablehead>
          <tablerow>
            <tablecell>item</tablecell>
            <tablecell align="center" size="small"></tablecell>
          </tablerow>
        </tablehead>
        <tablebody>
          {ctrl.items &&
            ctrl.items.map((item, idx) => (
              <tablerow key={item}>
                <tablecell>{item}</tablecell>
                <tablecell align="center" size="small">
                  <link onclick={() => deletebyindex(idx)}>delete</link>
                </tablecell>
              </tablerow>
            ))}
        </tablebody>
      </table>
    </tablecontainer>
  );
}

// react bootstrap
reactdom.render(
  <react.strictmode>
    <app />
  </react.strictmode>,
  rootelement
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@material-ui/core@latest/umd/material-ui.production.min.js"></script>

<div id="root"></div>

score:2

that's interesting question. because hook of react.usereducer is only take the initial value once, then it will maintain the state itself without setting value again.

so the idea is quite simple. you just simply set the your state again via the dispatch function you exported every time your data has been changed. here is the idea snippet:

let's create a exported function to update your state in usetable.ts:


return {
 setdata: data => dispatch(/* this is where you set your state again */)
}

now call setdata right your data changed in your main file:

const { setdata, ...others } = usetable(data);

react.useeffect(() => {
  if (data) {
   // this will sync your state with the latest data
   setdata(data);
  }
}, [data])

Related Query

More Query from same tag