首页 > 解决方案 > Ionic ReactJS-Redux 在 dispatch() 后不会重新渲染

问题描述

我将 Ionic 与 ReacJS 和 Redux 一起使用,我遇到了一个问题,即在第一次加载后元素没有重新渲染,即使状态发生了变化

这是代码

var categoriesTemplate = [{name:'Science', selected:false}, ... ]

const categories = useSelector((state: any) => {
    console.log("Updating State")
    let cat = state.categories
    for (let c of cat) {
      for (var category of categoriesTemplate) {
        if (category.name === c) {
          category.selected = true
        }
      }
    }
    return categoriesTemplate
  })



  const dispatch = useDispatch()

  const toggleCategory = (categorySelected: string) => {
    console.log("Toggle + Dispatch")
    for (var category of categories) {
      if (category.name === categorySelected) {
        category.selected = !category.selected
      }
    }

    let newCategories = categories.filter(c => { return c.selected }).map(c => c.name)    
    dispatch(setCategoriesState(newCategories))
  }

第一次运行加载所有categoriesTemplatewith 属性selectedfalse正常工作。
当我触发时,toggleCategory()我看到category(从 Redux after 获取dispatch())已正确更新为新值,但元素没有重新渲染。

我已经记录了状态更新和渲染代码

return (
      // Some React Components
      {
          categories.map(l => {
            console.log("Rendering")
            return ( ... )
        })
     }
     // Some React Components
     )

这是日志,如您所见,初始化正确呈现(第一个"Updating State"+ "Rendering"),但是在toggleCategory()状态触发器从 Redux 更新之后,"Updating State"而元素没有 "Rendering"

在此处输入图像描述

我错过了什么吗?

Ps 我没有从 reducer 和 action 发布代码,因为它可以工作,因为更新的值达到了我的category状态,并且不想添加熵,但如果你需要我可以发布它。

标签: reactjsionic-frameworkredux

解决方案


这是一个使用本地状态和重新选择来记忆具有选定属性的类别的示例。

const { createSelector } = Reselect;

const categoriesTemplate = [
  { name: 'Science', selected: false },
  { name: 'Technology', selected: false },
];
//selectors
const selectCategoriesWithSelected = createSelector(
  [
    (allCategories) => allCategories,
    (_, selectedNames) => selectedNames,
  ],
  (allCategories, selectedNames) =>
    allCategories.map((category) => ({
      ...category,
      selected: selectedNames.includes(category.name),
    }))
);
//Category is a pure component, does not realy matter in this case
//  all category items are re created when you toggle one
//  selected, that's just the way you implemented it and can be
//  implemented better
const Cateory = React.memo(function Category({
  toggleCategory,
  category,
}) {
  return (
    <li>
      <a
        onClick={(e) => {
          e.preventDefault();
          toggleCategory(category.name);
        }}
        href="/"
      >
        {category.name}{' '}
        {category.selected ? 'selected' : ''}
      </a>
    </li>
  );
});
function App() {
  const [
    selectedCategoriesNames,
    setSelectedCategoriesNames,
  ] = React.useState(() =>
    categoriesTemplate
      .filter(({ selected }) => selected)
      .map(({ name }) => name)
  );
  const toggleCategory = React.useCallback(
    (categoryName) =>
      //this is the action and reducer combined because
      //  we are using local state
      setSelectedCategoriesNames(//add or remove category name to selected list
        (selectedCategoriesNames) =>
          selectedCategoriesNames.includes(categoryName)
            ? selectedCategoriesNames.filter(
                (c) => c !== categoryName
              )
            : selectedCategoriesNames.concat(categoryName)
      ),
    []
  );
  const categoriesWithSelected = selectCategoriesWithSelected(
    categoriesTemplate,
    selectedCategoriesNames
  );
  return (
    <ul>
      {categoriesWithSelected.map((category) => (
        <Cateory
          key={category.name}
          category={category}
          toggleCategory={toggleCategory}
        />
      ))}
    </ul>
  );
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reselect/4.0.0/reselect.min.js"></script>


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

如果您只是将 categoriesTemplate 置于 state 并更改 selected 它会更简单并且可以优化:

const Cateory = React.memo(function Category({
  toggleCategory,
  category,
}) {
  console.log('rendering', category.name);
  return (
    <li>
      <a
        onClick={(e) => {
          e.preventDefault();
          toggleCategory(category.name);
        }}
        href="/"
      >
        {category.name}{' '}
        {category.selected ? 'selected' : ''}
      </a>
    </li>
  );
});
function App() {
  const [categories, setCategories] = React.useState([
    { name: 'Science', selected: false },
    { name: 'Technology', selected: false },
  ]);
  const toggleCategory = React.useCallback(
    (categoryName) =>
      //this is the action and reducer combined because
      //  we are using local state
      setCategories((categories) =>
        categories.map((c) =>
          c.name === categoryName
            ? { ...c, selected: !c.selected }
            : c
        )
      ),
    []
  );
  return (
    <ul>
      {categories.map((category) => (
        <Cateory
          key={category.name}
          category={category}
          toggleCategory={toggleCategory}
        />
      ))}
    </ul>
  );
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reselect/4.0.0/reselect.min.js"></script>


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


推荐阅读