Reactjs让list组件单独监视上下文

lymgl2op  于 2023-06-05  发布在  React
关注(0)|答案(2)|浏览(185)

当我们让每个组件监视它们自己状态时,是否存在任何性能影响或其他缺点
例如codepen

const DataContext = React.createContext();

const DataReducer = function (state, action) {
  switch (action.method) {
    case "add":
      state[action.id] = action.params;
      break;
    case "delete":
      delete state[action.id];
      break;
    default:
  }
  return { ...state };
};

const DataContextProvider = function (props) {
  let [data, setData] = React.useReducer(DataReducer, {});
  let [filter, setFilter] = React.useState(null);

  let value = {
    data: data,
    setData: setData,
    filter: filter,
    setFilter: setFilter
  };

  return (
    <DataContext.Provider value={value}>{props.children}</DataContext.Provider>
  );
};

const DelayInput = function (props) {
  let [timer, setTimer] = React.useState(null);

  let delay = props.delay ? props.delay : 1000;

  let delayChange = (e) => {
    if (timer) {
      clearTimeout(timer);
      setTimer(null);
    }
    setTimer(
      setTimeout(() => {
        props.onChange(e.target);
      }, delay)
    );
  };

  return (
    <input
      className="input"
      type="text"
      placeholder={props.placeholder}
      defaultValue={props.value}
      onChange={delayChange}
      name={props.name}
    />
  );
};

const Tool = function (props) {
  let { setData, setFilter } = React.useContext(DataContext);

  let handleChange = ((e) => {
    setFilter(e.value);
  });

  let handleClick = () => {
    setData({
      method: "add",
      id: new Date().getTime(),
      params: {
        x: Math.random(),
        y: Math.random(),
        last: new Date().getTime()
      }
    });
  };

  return (
    <div>
      <button className="button" onClick={handleClick}>
        Add
      </button>
      <DelayInput
        name="filter"
        placeholder="Filter"
        onChange={handleChange}
      />
    </div>
  );
};

const Row = function (props) {
  let { data, filter, setData } = React.useContext(DataContext);

  let handleClick = (() => {
    setData({
      method: "delete",
      id: props.id
    });
  });

  return React.useMemo(() => {
    if (filter && !props.id.toString().includes(filter)) {
      return null;
    }
    
    return (
      <tr>
        <td>{props.id}</td>
        <td>{data[props.id].x}</td>
        <td>{data[props.id].y}</td>
        <td>{data[props.id].last}</td>
        <td>
          <button className="button is-narrow" onClick={handleClick}>
            Delete
          </button>
        </td>
      </tr>
    );
  }, [filter, data[props.id]]);
};

const TableContent = function (props) {
  let { data } = React.useContext(DataContext);

  return React.useMemo(() => {
    return Object.keys(data).map((i) => {
      return <Row key={i} id={i} />;
    });
  }, [Object.keys(data)]);
};

const Container = function (props) {
  return (
    <DataContextProvider>
      <Tool />
      <table className="table is-bordered is-striped is-narrow is-hoverable is-fullwidth">
        <thead>
          <tr>
            <th>ID</th>
            <th>X</th>
            <th>Y</th>
            <th>Last Update</th>
            <th>Action</th>
          </tr>
        </thead>
        <tbody>
          <TableContent />
        </tbody>
      </table>
    </DataContextProvider>
  );
};

ReactDOM.createRoot(document.getElementById("root")).render(<Container />);

我的理解是,这样我们就可以确保这个组件只关心他的状态,就像观察者模式和责任分离一样。
上下文中的状态是数据
该组件成为所选状态的观察者并相应地改变,在我的示例中是Row组件
行组件还观察filtervalue,如果filter中的值发生变化,并且本身与filter不匹配,则会隐藏自己。
并且由于该行观察自己的状态,我们可以自由地记忆组件
这种方法有什么缺点吗?

kb5ga3dv

kb5ga3dv1#

因此,当你像这样使用上下文时,每个使用上下文的子组件都会在数据的任何部分发生变化时重新呈现**。
这意味着即使data[props.id]没有改变,如果data的任何部分改变了,它也会重新呈现。
我建议使用Redux。其实前几天我只是第一次为自己使用,而且比我预期的要容易。与useContext不同,它允许您订阅状态的一小部分,并且仅在您依赖的状态发生变化时才重新呈现。有关详细信息,请参见useSelector。

7fhtutme

7fhtutme2#

如果上下文API使用得当,那么只有那些使用上下文的组件才会在上下文发生变化时重新呈现。我建议你看看react-context-slices,它是一个以最佳方式使用React Context的库,允许你轻松地定义Context切片,并使用0样板。以这种方式,只有获取Context的每个切片的组件才会在其值改变时更新。你这样做:

// slices.js
import getHookAndProviderFromSlices from "react-context-slices"
export const {useSlice, Provider} = getHookAndProviderFromSlices({
  count: {initialArg: 0},
  // rest of slices of Context you want to define
})
// app.js
import {useSlice} from "./slices"
const App = () => {
  const [count, useCount] = useSlice("count")
  return <>
    <button onClick={() => setCount(c => c + 1)}>+</button>{count}
  </>
}

希望这对任何计划使用上下文的人都有帮助。

相关问题