【问题标题】:Populating a Material-UI dropdown with Redux store data使用 Redux 存储数据填充 Material-UI 下拉列表
【发布时间】:2021-03-20 20:18:28
【问题描述】:

我正在尝试让我的 Redux 存储字段自动填充我已导入的方法。我是否以正确的方式来完成这项工作?我需要为每个字段创建映射选项吗?

我的每个下拉列表都插入了 PopulateDropdown 列表和每个下拉列表中的字段,但我需要根据 id 和文本将它们拆分。

我是否在下面正确访问了我的 redux 商店?我使用 const fields = useSelector(state => state.fields);

在我的函数组件上声明了数组

更新 我将方法插入到下拉列表应该在的位置,但是我认为我没有正确访问导致问题的数据。字段数组已被分解为每个下拉菜单的六个不同字段,并为每个字段创建了不同的映射选项。

我需要做什么才能将数据导入方法中?我见过的示例在组件上声明了静态数组,而不是使用 Redux 存储。

const fields = useSelector(state => state.fields);
// can destructure individual fields
const { diveSchoolList, currentList, regionList, diveTypeList, visibilityList, diveSpotList } = fields;

我已经导入的populateDropdown 方法

export const PopulateDropdown = ({ dataList = [], mappingOptions, name, label }) => {

const { title, value } = mappingOptions;

return (
<FormControl style={{ width: 200 }} >
  <InputLabel id={label}>{label}</InputLabel>
  <Select labelId={label} name={name} > 
  {dataList.map((item) => (
  <MenuItem value={item[value]}>{item[title]}</MenuItem>
  ))}
  </Select>
</FormControl>
);
};

导入的下拉菜单

       <PopulateDropdown
           dataList={diveType}
           mappingOptions={mappingOptions}
           name="fieldName"
           label="Select dive type"
           value={dive.typeID}
           onChange={handleChange}/>

更新

我已经更新了我的 action、reducer 和 populateFields 方法,但是我仍然无法将 redux 数据映射到我的两个属性字段。在 Redux 树中,当我在控制台记录它们时,这些字段应该位于 fields.data.fieldlists 下。

我应该以什么方式将它们填充到 titleProperty 等中?它目前看起来可能正在填充,但是一个大框会下拉,我看不到里面的任何值。

// select user object from redux
        const user = useSelector(state => state.user);

        // get the object with all the fields
        const fields = useSelector(state => state.fields);

        // can destructure individual fields
        const { diveSchoolList = [],
                currentList = [],
                regionList = [],
                diveTypeList = [],
                visibilityList = [],
                diveSpotList = [],
                marineTypeList = [],
                articleTypeList = []
        } = fields;

 .........


<PopulateDropdown
   dataList={fields.data.diveTypeList} // the options array
   titleProperty={fields.data.diveTypeList.diveTypeID} // option label property
   valueProperty={fields.data.diveTypeList.diveType} // option value property
   label="Dive Type Name" // label above the select
   placeholder="Select dive type" // text show when empty
   value={dive.typeID} // get value from state
   onChange={handleChange(setDive.typeID)} // update state on change
/>

【问题讨论】:

  • 您需要使用 redux-toolkit 中的 useSelector 或 react-redux 中的 connect 函数将 redux 数据注入到您的组件中。你做得对吗?
  • 我推荐你使用 redux 钩子来完成这个。如前所述,使用 useSelector 获取数据并使用 useDispatch 调度操作。你确定你做对了吗?
  • 这个答案会有所帮助:stackoverflow.com/questions/66648507/…
  • 嗨 Jimbo,I helped you 几周前建立了 redux 商店。现在看起来怎么样?如果您可以分享 Github 存储库或 CodeSandbox,它将帮助我更好地了解数据结构是什么样的。我绝对认为我们可以创建一个通用组件,只需几个配置道具即可适用于所有 6 个列表。
  • 看起来你所拥有的大部分是正确的!我认为您实际上不需要将 name 属性传递给选择。另外,我不会创建mappingOptions 对象,而是让PopulateDropdown 组件具有两个单独的属性valuePropertytitleProperty。我可以给你写一个答案,但它不像上一个那样深入,因为你已经掌握了这个窍门!我看到您在一个 API 调用中获取所有字段数据,这很棒。

标签: reactjs redux react-redux material-ui


【解决方案1】:

您的PopulateDropdown 组件看起来是正确的,只是我们需要它使用我们作为道具传递下来的valueonChange

我个人的偏好是使用单独的属性 valuePropertytitleProperty 而不是传递单个 mappingOptions。这样你就不需要为每个下拉列表创建对象,你只需在 JSX 中设置两个属性。如果您对数据进行规范化,使每个列表的元素具有相同的属性idlabel,则可以完全摆脱这部分。

<PopulateDropdown
  dataList={diveTypeList} // the options array
  titleProperty={"diveTypeId"} // option label property
  valueProperty={"diveType"} // option value property
  label="Dive Type Name" // label above the select
  placeholder="Select dive type" // text show when empty
  value={dive.typeID} // get value from state
  onChange={handleChange("typeId")} // update state on change
/>
export const PopulateDropdown = ({
  dataList = [],
  valueProperty,
  titleProperty,
  label,
  ...rest // can just pass through all other props to the Select
}: Props) => {
  return (
    <FormControl style={{ width: 200 }}>
      <InputLabel id={label}>{label}</InputLabel>
      <Select {...rest} labelId={label}>
        {dataList.map((item) => (
          <MenuItem value={item[valueProperty]}>{item[titleProperty]}</MenuItem>
        ))}
      </Select>
    </FormControl>
  );
};

看起来currentId 中的 id 实际上是 number,因此在代码中的某个时刻,您可能需要将其转换为 parseInt,因为 e.target.value 始终是 string,尽管可能后端可以处理。


加载 API 数据

看起来您已经知道如何在一个 API 调用中获取所有字段,这很棒。您将其保存到字段 reducer 上的属性 fields 中,该字段创建结构 state.fields.fields。由于您要替换整个状态,因此您可以将整个事物作为整个切片状态返回。

您可以使用空数组初始化您的状态对象,或者您可以使用一个空对象{} 作为您的初始状态,并在您解构数组时回退到一个空数组,例如const {diveSchoolList = [], currentList = []} = fields

export const requireFieldData = createAsyncThunk(
  "fields/requireData", // action name
  // don't need any argument because we are now fetching all fields
  async () => {
    const response = await diveLogFields();
    return response.data;
  },
  // only fetch when needed: https://redux-toolkit.js.org/api/createAsyncThunk#canceling-before-execution
  {
    // _ denotes variables that aren't used -  the first argument is the args of the action creator
    condition: (_, { getState }) => {
      const { fields } = getState(); // returns redux state
      // check if there is already data by looking at the didLoadData property
      if (fields.didLoadData) {
        // return false to cancel execution
        return false;
      }
    }
  }
);
const fieldsSlice = createSlice({
  name: "fields",
  initialState: {
    currentList: [],
    regionList: [],
    diveTypeList: [],
    visibilityList: [],
    diveSpotList: [],
    diveSchoolList: [],
    marineTypeList: [],
    articleTypeList: [],
    didLoadData: false,
  },
  reducers: {},
  extraReducers: {
    // picks up the pending action from the thunk
    [requireFieldData.pending.type]: (state) => {
      // set didLoadData to prevent unnecessary re-fetching
      state.didLoadData = true;
    },
    // picks up the success action from the thunk
    [requireFieldData.fulfilled.type]: (state, action) => {
      // want to replace all lists, there are multiple ways to do this
      // I am returning a new state which overrides any properties
      return {
        ...state,
        ...action.payload
      }
    }
  }
});

所以在组件中我们现在只需要调用一个动作而不是循环遍历字段。

useEffect(() => {
    dispatch(requireFieldData());
}, []);

【讨论】:

  • 如果下拉列表看起来正在填充,但是我实际上无法读取值,因为它只是一个大的白框下拉,听起来错误与什么有关?
  • 听起来我们为每个选项都获得了一个MenuItem,但我们没有提取正确的标签(这将是MenuItem 上的children 属性)。
  • 本部分:{item[titleProperty]}
  • 在这种情况下我应该以什么方式填充它们?看起来它可能会用 valueProp={fields.data.diveTypeList.diveType) 之类的东西填充它们,但是当页面重新呈现像“fields.data 未定义”并且我的商店没有填充时,我遇到了错误。
  • 我想我可以输入 dataList 名称,因为它在顶部被解构,然后是特定的行名称。
猜你喜欢
  • 2023-03-19
  • 2013-05-10
  • 1970-01-01
  • 1970-01-01
  • 2016-10-07
  • 2014-08-27
  • 1970-01-01
  • 1970-01-01
  • 2012-08-13
相关资源
最近更新 更多