智能/哑组件模式的正确用法是什么?

Ale*_*min 2 reactjs redux react-redux

我有一个 DropdownFilter演示组件,我想将其重用于多个不同的过滤器。

const DropdownFilter: React.FC<DropdownFilterProps> = ({
  filterValue,
  filterOptions,
  onClickHandler,
}) => {
  return (
    <Dropdown>
      <Dropdown.Toggle id="dropdown">{filterValue}</Dropdown.Toggle>
      <Dropdown.Menu>
        {filterOptions.map((option, i) => (
          <Dropdown.Item onClick={() => onClickHandler(option)} key={i}>
            {option}
          </Dropdown.Item>
        ))}
      </Dropdown.Menu>
    </Dropdown>
  );
};

export default DropDownFilter;
Run Code Online (Sandbox Code Playgroud)

每个过滤器的状态由filterReducerin控制Redux。这是否意味着我必须创建多个容器组件才能获取每个单独过滤器的数据?

const StatusFilterContainer: React.FC = () => {
  const statusFilterValue = useSelector(getStatusFilterValue);
  const statusFilterOptions = ["Show All", "Pending", "Completed", "Cancelled"];

  const onStatusFilterChange = (filterValue: string): void => {
    dispatch(setStatusFilterValue(filterValue));
  };

  return (
    <>
      <DropDownFilter
        onClickHandler={onStatusFilterChange}
        filterOptions={statusFilterOptions}
        filterValue={statusFilterValue}
      />
    </>
  );
};

export default StatusFilterContainer;
Run Code Online (Sandbox Code Playgroud)
const TypeFilterContainer: React.FC = () => {
  const typeFilterValue = useSelector(getTypeFilterValue);
  const typeFilterOptions = ["Show All", "Refill", "Withdrawal"];

  const onStatusFilterChange = (filterValue: string): void => {
    dispatch(setTypeFilterValue(filterValue));
  };

  return (
    <>
      <DropDownFilter
        onClickHandler={onStatusFilterChange}
        filterOptions={typeFilterOptions}
        filterValue={typeFilterValue}
      />
    </>
  );
};

export default TypeFilterContainer;
Run Code Online (Sandbox Code Playgroud)

Dre*_*ese 6

智能/哑组件模式的正确用法是什么?

IMO 正确的用法通常是尽可能限制状态和逻辑的范围,仅在必要时在 API 上公开状态和变更器(即提升状态)。还有关注点分离的考虑。例如,提交到后端的表单的某些输入字段的值并不是整个应用程序真正关心的问题,因此将其保持在组件状态很好,但是像经过身份验证的用户这样的东西可能是整个应用程序中的任何组件都关心的问题。应用程序,因此将其抬起。

重复的 WET(将所有内容写入两次)代码:

通过转换重复的逻辑来创建更干燥的解决方案相当简单。基本上收集抽象上相同/共同的内容(即使用选择器、调度操作函数)并分解出不同的内容(即选项、实际选择器和操作),这些可以作为道具传递。

抽象地说,这是一个提供渲染选项、选择某些状态并在选项更改时分派某些操作的组件。

const FilterContainer: React.FC = ({
  options,
  selectorFn,
  setFilterValueAction,
}) => {
  const filterValue = useSelector(selectorFn);

  const onStatusFilterChange = (filterValue: string): void => {
    dispatch(setFilterValueAction(filterValue));
  };

  return (
    <>
      <DropDownFilter
        onClickHandler={onStatusFilterChange}
        filterOptions={options}
        filterValue={filterValue}
      />
    </>
  );
};
Run Code Online (Sandbox Code Playgroud)

请注意,现在这与原始DropDownFilter组件非常相似(此时它基本上只是一个代理),我们可以直接将一点逻辑移入DropDownFilter或来自 JSX,具体取决于您的哲学倾向)。

const DropdownFilter: React.FC<DropdownFilterProps> = ({
  filterOptions,
  stateSelector,
  onChangeAction,
}) => {
  const filterValue = useSelector(stateSelector);

  const onStatusFilterChange = (filterValue: string): void => {
    dispatch(onChangeAction(filterValue));
  };

  return (
    <Dropdown>
      <Dropdown.Toggle id="dropdown">{filterValue}</Dropdown.Toggle>
      <Dropdown.Menu>
        {filterOptions.map((option, i) => (
          <Dropdown.Item onClick={() => onStatusFilterChange(option)} key={i}>
            {option}
          </Dropdown.Item>
        ))}
      </Dropdown.Menu>
    </Dropdown>
  );
};
Run Code Online (Sandbox Code Playgroud)

我们现在已经内部化了选择器、调度和渲染逻辑,只需要选项、选择器来获取状态,以及选项更改时要采取的操作。

用法:

const StatusFilterContainer: React.FC = () => (
  <DropdownFilter
    filterOptions={["Show All", "Pending", "Completed", "Cancelled"]}
    onChangeAction={setStatusFilterValue}
    stateSelector={getStatusFilterValue}
  />
);

const TypeFilterContainer: React.FC = () => (
  <DropdownFilter
    filterOptions={["Show All", "Refill", "Withdrawal"]}
    onChangeAction={setTypeFilterValue}
    stateSelector={getTypeFilterValue}
  />
);
Run Code Online (Sandbox Code Playgroud)