【问题标题】:Passing data up through nested Components in React通过 React 中的嵌套组件向上传递数据
【发布时间】:2020-05-21 17:33:03
【问题描述】:

以一个想法开头;我想我可能需要一个递归组件,但这超出了我目前使用原生 js 和 React 的能力,所以我觉得我现在对 React 有了瑞士奶酪的理解。

问题

我有一个包含metafield 对象的metafields 数组,其结构如下:

{
  metafields: [
    { 0: 
      { namespace: "namespaceVal", 
        key: "keyVal", 
        val: [
          0: "val1", 
          1: "val2", 
          2: "val3" 
        ]
      }
    }, 
    ...
  ]
}

我的代码将metafields 映射到卡片中,并且在每张卡片中都有一个组件<MetafieldInput metafields={metafields['value']} />,在该组件中,值数组被映射到输入字段。总体来说是这样的:

// App
render() {
  const metafields = this.state.metafields;
  return (
    {metafields.map(metafield) => (
      <MetafieldInputs metafields={metafield['value']} />
    )}
  )
}

//MetafieldInputs
this.state = { metafields: this.props.metafields}

render() {
  const metafields = this.state;
  return (
    {metafields.map((meta, i) => (
      <TextField 
        value={meta}
        changeKey={meta}
        onChange={(val) => {
          this.setState(prevState => {
            return { metafields: prevState.metafields.map((field, j) => {
              if(j === i) { field = val; }
              return field;
            })};
          });
        }}
      />
    ))}
  )
}

到目前为止,一切都显示正确,我可以更改输入!然而,更改一次发生一个,因为我按下一个键然后我必须单击返回输入以添加另一个字符。似乎所有内容都被重新渲染,这就是为什么我必须重新点击输入以进行另一次更改。

我能以这种方式使用组件吗?感觉就像我正在努力嵌套组件,但我读过的所有内容都说不要嵌套组件。我是不是把这个问题复杂化了?我唯一的解决方案是撕掉 React 部分并将其带到纯 JavaScript 中。

非常感谢您的指导!

【问题讨论】:

  • 尝试为每个输入字段添加一个key 属性。

标签: javascript reactjs components


【解决方案1】:

我的建议是外包 onChange 处理程序,代码可以更容易理解。

主要是 React 在调用 setState() 后不会立即更新状态,它会执行批处理作业。因此,可能会发生多个setState 调用正在访问一个参考点。如果直接改变状态,可能会导致混乱,因为其他状态可以在执行批处理作业时使用更新的状态。

此外,如果您在 App 级别输出 onChange 处理程序,您可以将 MetafieldInputs 更改为功能组件,而不是基于类的组件。基于功能的组件成本低于基于类的组件,并且可以提高性能。

以下是更新的代码,经过测试。我假设你使用 Material UI 的 TextField,但 onChangeHandler 也应该在你自己的组件中工作。

// Full App.js
import React, { Component } from 'react';
import MetafieldInputs from './MetafieldInputs';

class App extends Component {
    state = {
        metafields: [
            {
                metafield:
                {
                    namespace: "namespaceVal",
                    key: "keyVal",
                    val: [
                        { '0': "val1" },
                        { '1': "val2" },
                        { '2': "val3" }
                    ]
                }
            },
        ]
    }

    // will never be triggered as from React point of view, the state never changes
    componentDidUpdate() {
        console.log('componentDidUpdate')
    }

    render() {
        const metafields = this.state.metafields;
        const metafieldsKeys = Object.keys(metafields);
        const renderInputs = metafieldsKeys.map(key => {
            const metafield = metafields[key];
            return <MetafieldInputs metafields={metafield.metafield.val} key={metafield.metafield.key} />;
        })
        return (
            <div>
                {renderInputs}
            </div>
        )
    }
}

export default App;

// full MetafieldInputs
import React, { Component } from 'react'
import TextField from '@material-ui/core/TextField';

class MetafieldInputs extends Component {
    state = {
        metafields: this.props.metafields
    }

    onChangeHandler = (e, index) => {
        const value = e.target.value;
        this.setState(prevState => {
            const updateMetafields = [...prevState.metafields];
            const updatedFields = { ...updateMetafields[index] }
            updatedFields[index] = value
            updateMetafields[index] = updatedFields;
            return { metafields: updateMetafields }
        })
    }

    render() {
        const { metafields } = this.state;
        // will always remain the same
        console.log('this.props', this.props)
        return (
            <div>
                {metafields.map((meta, i) => {
                    return (
                        <TextField
                            value={meta[i]}
                            changekey={meta}
                            onChange={(e) => this.onChangeHandler(e, i)}
                            // generally it is not a good idea to use index as a key.
                            key={i}
                        />
                    )
                }
                )}
            </div>
        )
    }
}

export default MetafieldInputs

同样,如果你将onChangeHandler 外包到App 类,MetafieldInputs 可以是一个纯函数组件,所有的状态管理都可以在App 类中完成。

另一方面,如果您想保持一个纯净的App 类,您还可以将元字段存储到 MetafieldInputs 类中,以防您的应用程序中可能需要一些其他逻辑。

例如,您的应用程序比示例呈现更多的组件,并且在发生某些事情之前不应呈现 MetafieldInputs。如果从服务器端获取数据,最好在需要的时候获取数据,而不是在 App 组件中获取所有数据。

【讨论】:

  • 一旦我将 onChange 处理程序移动到应用程序并将其传递给 MetafieldsInput 子项,我的更改就不再注册。当我尝试分配 const value = e.target.value 时,我得到一个未定义的错误。
  • 您可能需要仔细检查您的状态管理是否正确,因为对象是嵌套的。
  • 我能够让它工作,但它就像我的代码一样,在再次渲染之前只接受一个输入,所以我必须点击回输入字段。这是将 onChange 处理程序移动到应用程序中并防止这种情况发生的地方吗?
  • 在这里结帐:github.com/mui-org/material-ui/issues/783。 “我为我的情况解决了这个问题,您需要为该文本字段提供一个唯一的 id。”,我也尝试过,然后输入字段不会再次失去焦点。我也更新了代码,你可以试试。
  • 这里使用的是嵌套对象,metafields中的对象是键值对。例如,metafields['0'] 将为您提供namespacekeyval 等,您必须确定您引用了metafields 中的哪些对象。尝试使用调试器或添加更多的console.log,您可以轻松识别是否引用了错误的对象或尚未成功引用您要使用的对象。
【解决方案2】:

您需要在应用级别执行 onChange。您应该只将 onChange 函数传递给 MetaFieldsInput 并在渲染时始终使用this.props.metafields

【讨论】:

  • 一旦我将 onChange 函数移动到应用程序级别并将其传递给孩子,我的更改将不再被注册。
猜你喜欢
  • 1970-01-01
  • 2020-04-03
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-29
  • 2021-12-07
  • 2019-03-13
相关资源
最近更新 更多