React 和 Redux 工具包:当我想使用过滤器操作时松开状态

React and Redux toolkit : loose the state when I want to use filter action

我正在尝试创建一个按钮来根据输入的值过滤数据。

我在 header 组件中创建了一个按钮,在主组件中创建了我的项目列表。

我的问题是,当我从 header 中的按钮发送 'projectFilter' 操作时,它过滤了数据,但我丢失了初始状态。

有我的项目切片器

import { createSlice } from "@reduxjs/toolkit";

let lastId = 2;

const slice = createSlice({
  name: "projects",
  initialState: [],
  reducers: {
    projectAdded: (state, action) => {
      state.push({
        id: ++lastId,
        name: action.payload.name,
        gender: action.payload.gender,
      });
    },
    projectFilter: (state, action) => {
      return state.filter((project) => project.id === action.payload.id);
    },
  },
});

export const { projectAdded, projectFilter } = slice.actions;
export default slice.reducer; 

添加项目和筛选项目有我的两个按钮

function Header() {
  const dispatch = useDispatch();

  const [value, setValue] = useState(0);

  return (
    <div className="header">
      <button
        onClick={() =>
          dispatch(
            projectAdded({ name: "project name", gender: "project gender" })
          )
        }
      >
        Add project
      </button>
      <input type="number" onChange={(event) => setValue(event.target.value)} />
      <button onClick={() => dispatch(projectFilter({ id: Number(value) }))}>
        Filter
      </button>
    </div>
  );
}

export default Header;

最后是我在页面中呈现的项目列表

function Main() {
  const projects = useSelector((state) => state.projects);
  return (
    <div className="main">
      {projects.map((project) => (
        <p key={project.id}>
          {project.id} {project.name} {project.gender}
        </p>
      ))}
    </div>
  );
}

export default Main;

你能帮我在不丢失所有状态的情况下过滤数据吗?提前谢谢你

操作与选择器

当你发送一个动作时,你正在永久地改变状态。您不希望仅将 state.projects 替换为与当前过滤器匹配的项目。您希望将所有项目都保留在该州,而 select 只保留匹配的项目。这是您在 Main.

中使用 useSelector 中的 selector 函数执行的操作

存储当前过滤器

您的 selector 函数需要知道过滤器 ID 才能过滤匹配项。您可以通过 useState 将该 id 存储在组件中,或者您可以将过滤器 id 作为 属性 存储在您的 redux 存储中(在项目切片或另一个切片中)。

如果您正在编辑过滤器并显示同一组件的过滤结果,我会推荐 useState 方法。使用两个单独的组件,您仍然可以这样做,但是状态必须存储在共享 parent 中并通过 props 向下传递。我不知道你的 MainHeader 组件之间的关系,所以对于这种情况,我建议将当前过滤器存储在 redux 中。您将 dispatch 设置当前过滤器的操作。基本上,该操作只是更新存储的过滤器 ID。应用过滤器的工作仍然转到 selector.

lastId

Reducer 不应依赖外部状态。您不希望 lastId 只是文件中的某个变量。您需要从状态本身获取一个 id。您可以在当前项目中查找最大 id,或者您可以将 lastId 存储为状态中的 属性。

代码

切片

import { createSlice } from "@reduxjs/toolkit";

const initialState: {
    projects: [],
    lastId: 2, // why not 0 or 1?
    filter: null, // is just the id right now, but could be a complex object
}

const slice = createSlice({
  name: "projects",
  initialState,
  reducers: {
    projectAdded: (state, action) => {
      state.projects.push({
        id: ++state.lastId,
        name: action.payload.name,
        gender: action.payload.gender,
      });
    },
    projectFilter: (state, action) => {
      state.filter = action.payload.id;
    },
  },
});

export const { projectAdded, projectFilter } = slice.actions;
export default slice.reducer;

Header

无变化

主要

function Main() {
  const projects = useSelector(
      // could move this function to another file
      // this could be written more concisely, but I'm trying to be clear instead
      (state) => {
          const all = state.projects.projects;
          const filterId = state.projects.filter;
          if ( filterId === null ) {
              return all;
          }
          else {
            return all.filter( project => project.id === filterId );
          }
  });
  return (
    <div className="main">
      {projects.map((project) => (
        <p key={project.id}>
          {project.id} {project.name} {project.gender}
        </p>
      ))}
    </div>
  );
}

export default Main;