【问题标题】: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 函数标识是稳定的并且不会 更改重新渲染。这就是为什么从 useEffect 或 useCallback 依赖列表。

您会看到在初始渲染时调用了一次 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, displayMl 和useMemo。

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
    相关资源
    最近更新 更多