【问题标题】:Conditionally sending an object of key-value (array) from child to parent component有条件地将键值(数组)对象从子组件发送到父组件
【发布时间】:2021-08-15 10:21:07
【问题描述】:

我正在尝试将键值对的对象传递给父组件,该对象的值是字符串数组。这些值将来自 UI-Kitten 的多选组件,并将作为对象传递给 Parent。

我了解数据在父组件和子组件之间的传递方式以及 useEffect 在组件中用于 componentDidMount 或效果的任何条件触发的用法。但是这个案例比较复杂。

我可能会将整个子组件转储到父组件中,因为我尝试复制类似的东西并且它有效,即每当用户从多选中选择/取消选择一个选项时,我都会更新键值对的对象。希望我可以使用 Child-Parent,这样 Parent 就不会因为 Child 组件很长而混乱。

我希望我能得到一些帮助。谢谢!

父组件(状态,techniqueExperience,未更新):

  const [technologyExperience, setTechnologyExperience] = React.useState({
    game: [],
    web: [],
    mobile: [],
    database: [],
    machineLearning: []
  });

  const getSelections = data => {
    setTechnologyExperience(prevData => {
      return { ...prevData, ...data };
    });

    // Only prints on first load
    console.log('parent.getSelections', technologyExperience);
  };

  React.useEffect(() => {
    // Only prints on first load
    console.log('parent.useEffect.getSelections', technologyExperience);
  }, [getSelections]);


// calling of the Child component
<InputBackgroundSelect getSelections={getSelections} />

子组件

const InputBackgroundSelect = props => {
  // All the values needed to pass to Parent
  const [gameIndex, setGameIndex] = React.useState([]);
  const displayGameDev = gameIndex.map(index => {
    return gameDevData[index.row];
  });

  const [webIndex, setWebIndex] = React.useState([]);
  const displayWebDev = webIndex.map(index => {
    return webDevData[index.row];
  });

  const [mobileIndex, setMobileIndex] = React.useState([]);
  const displaymobileDev = mobileIndex.map(index => {
    return mobileDevData[index.row];
  });

  const [dbIndex, setDBIndex] = React.useState([]);
  const displayDb = dbIndex.map(index => {
    return dbData[index.row];
  });

  const [mlIndex, setMLIndex] = React.useState([]);
  const displayMl = mlIndex.map(index => {
    return mlData[index.row];
  });

  // As for the conditional effect, I have tried using
  // [displayGameDev, displayWebDev, displaymobileDev, displayDb, displayMl] too.
  React.useEffect(() => {
    // Only prints on first load but not when selecting/deselecting options
    console.log('Calling props.getSelections'); 

    props.getSelections({
      game: displayGameDev,
      web: displayWebDev,
      mobile: displaymobileDev,
      database: displayDb,
      machineLearning: displayMl
    });
  }, [setGameIndex, setWebIndex, setMobileIndex, setDBIndex, setMLIndex]);

// An example of the 5 repeated Selects
      <Select
        label='Mobile Development'
        style={styles.selectInput}
        multiSelect={true}
        selectedIndex={mobileIndex}
        onSelect={index => setMobileIndex(index)} // Fires when user selects an option from the Select
        placeholder='Select'
        value={displaymobileDev.join(', ')}
      >
        {mobileDevData.map((value, key) => (
          <SelectItem key={key} title={value} />
        ))}
      </Select>

【问题讨论】:

  • useEffect 在父级中触发时,您是否至少看到更新的technologyExperience?顺便说一句,您对该效果的依赖有误,它应该是 technologyExperience
  • @DrewReese :如果使用此问题中的代码,则不会打印。仅在首次加载时打印。我已将依赖项更改为technologyExperience,也无济于事。
  • 您是否验证了父级中的getSelections 正在被调用?如果您在状态更新之前将控制台日志放在那里,您会看到日志吗?您是否看到 入队状态更新后的日志?
  • 显然,getSelections 只在屏幕显示时调用一次,其他 2 个useEffect 声明也是如此。我已经更新了上面的代码以获得更好的说明。验证父级中的getSelections 仅在屏幕显示时调用一次。在状态更新之前,是的,我看到了日志 parent.getSelections ...parent.useEffect.getSelections ... 不,我在排队状态更新后看不到日志
  • 修复子组件中的useEffect 依赖,这样效果才能真正正确运行。您似乎对 React 钩子依赖项是什么有误解。除了mobileIndex,您实际上是否更新了子其他中的任何状态?

标签: javascript reactjs react-native react-hooks use-effect


【解决方案1】:

问题

看来您的大部分问题是对 React 钩子依赖项的误解。您正在使用状态更新器函数作为依赖项,但这些是稳定的引用,即它们在渲染之间是相同的,因此如果包含在挂钩的依赖项数组中,它们将不会触发挂钩回调。

useState

注意

React 保证 setState 函数标识是稳定的并且不会 更改重新渲染。这就是为什么从 useEffectuseCallback 依赖列表。

您会看到在初始渲染时调用了一次 useEffect 回调,但由于依赖项是稳定的,它们永远不会更新,以后也不会再次触发效果回调。

其他问题

InputBackgroundSelect 组件中,displayGameDev, displayWebDev, displaymobileDev, displayDb, displayMl, getSelections 变量是 useEffect 的依赖项,用于getSelections 回调,它们是在函数体中声明的的组件。这意味着它们在每个渲染周期都会重新声明,从而触发一些渲染循环。

解决方案

修复父组件和子组件中的useEffect 依赖项。 Hook 依赖项基本上是回调中引用的任何内容,它们会在渲染之间进行更改。

父母

React.useEffect(() => {
  console.log('parent.useEffect.getSelections', technologyExperience);
}, [technologyExperience]);

孩子

React.useEffect(() => {
  getSelections({
    game: displayGameDev,
    web: displayWebDev,
    mobile: displaymobileDev,
    database: displayDb,
    machineLearning: displayMl
  });
}, [displayGameDev, displayWebDev, displaymobileDev, displayDb, displayMl, getSelections]);

关于 React 状态更新的附加说明,它们是异步的,因此您无法在 将更新入队后立即记录您的状态,它仍然只是来自 的状态值当前的 渲染周期,而不是您刚刚为下一个 渲染排队的周期。

const getSelections = data => {
  setTechnologyExperience(prevData => {
    return { ...prevData, ...data };
  });

  // This will be current state, not next state
  console.log('parent.getSelections', technologyExperience);
};

只需使用useEffect with dependency on technologyExperience` 来记录更新时间。 (即上面修复的问题?

要修复渲染循环,您需要记住 getSelections 回调和 useCallback

const getSelections = React.useCallback(data => {
  setTechnologyExperience(prevData => ({ ...prevData, ...data }));
}, []);

这里列出的displayGameDev, displayWebDev, displaymobileDev, displayDb, displayMluseMemo

const [gameIndex, setGameIndex] = React.useState([]);
const displayGameDev = React.useMemo(
  () => gameIndex.map((index) => gameDevData[index.row]),
  [gameIndex]
);

const [webIndex, setWebIndex] = React.useState([]);
const displayWebDev = React.useMemo(
  () => webIndex.map((index) => webDevData[index.row]),
  [webIndex]
);

const [mobileIndex, setMobileIndex] = React.useState([]);
const displaymobileDev = React.useMemo(
  () => mobileIndex.map((index) => mobileDevData[index.row]),
  [mobileIndex]
);

const [dbIndex, setDBIndex] = React.useState([]);
const displayDb = React.useMemo(
  () => dbIndex.map((index) => dbData[index.row]),
  [dbIndex]
);

const [mlIndex, setMLIndex] = React.useState([]);
const displayMl = React.useMemo(
  () => mlIndex.map((index) => mlData[index.row]),
  [mlIndex]
);

这是一个分叉的Expo Snack 的链接。

【讨论】:

    猜你喜欢
    • 2022-08-02
    • 2017-09-13
    • 2020-10-11
    • 2021-05-13
    • 2020-09-28
    • 1970-01-01
    • 1970-01-01
    • 2019-10-24
    • 2019-09-07
    相关资源
    最近更新 更多