简体   繁体   English

通过 React 中的嵌套组件向上传递数据

[英]Passing data up through nested Components in React

Prefacing this with a thought;以一个想法开始; I think I might require a recursive component but that's beyond my current ability with native js and React so I feel like I have Swiss cheese understanding of React at this point.我想我可能需要一个递归组件,但这超出了我目前使用原生 js 和 React 的能力,所以我觉得此时我对 React 有了瑞士奶酪的理解。

The problem :问题

I have an array of metafields containing metafield objects with the following structure:我有一个包含元metafield对象的元metafields数组,其结构如下:

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

My code maps metafields into Cards and within each card lives a component <MetafieldInput metafields={metafields['value']} /> and within that component the value array gets mapped to input fields.我的代码将元metafields映射到卡片中,并且在每张卡片中都有一个组件<MetafieldInput metafields={metafields['value']} />并且在该组件中,值数组被映射到输入字段。 Overall it looks like:整体看起来像:

// 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;
            })};
          });
        }}
      />
    ))}
  )
}

Up to this point everything displays correctly and I can change the inputs!到目前为止,一切都显示正确,我可以更改输入! However the change happens one at a time, as in I hit a key then I have to click back into the input to add another character.但是,更改一次一个,因为我按了一个键,然后我必须单击回输入以添加另一个字符。 It seems like everything gets re-rendered which is why I have to click back into the input to make another change.似乎一切都被重新渲染,这就是为什么我必须单击回输入进行另一次更改。

Am I able to use components in this way?我可以以这种方式使用组件吗? It feels like I'm working my way into nesting components but everything I've read says not to nest components.感觉就像我正在努力嵌套组件,但我读过的所有内容都说不要嵌套组件。 Am I overcomplicating this issue?我是不是把这个问题复杂化了? The only solution I have is to rip out the React portion and take it to pure javascript.我唯一的解决方案是撕掉 React 部分并将其用于纯 javascript。

guidance would be much appreciated!指导将不胜感激!

You need to do the onChange at the app level.您需要在应用程序级别执行 onChange。 You should just pass the onChange function into MetaFieldsInput and always use this.props.metafields when rendering您应该将 onChange 函数传递给 MetaFieldsInput 并在渲染时始终使用this.props.metafields

My suggestion is that to out source the onChange handler, and the code can be understood a little bit more easier.我的建议是取出 onChange 处理程序的源代码,这样可以更容易地理解代码。

Mainly React does not update state right after setState() is called, it does a batch job.主要是 React 不会在setState()调用后立即更新状态,它会执行批处理作业。 Therefore it can happen that several setState calls are accessing one reference point.因此,可能发生多个setState调用正在访问一个参考点。 If you directly mutate the state, it can cause chaos as other state can use the updated state while doing the batch job.如果直接改变状态,可能会造成混乱,因为其他状态可以在执行批处理作业时使用更新后的状态。

Also, if you out source onChange handler in the App level, you can change MetafieldInputs into a functional component rather than a class-bases component.此外,如果您在 App 级别输出 onChange 处理程序,您可以将 MetafieldInputs 更改为功能组件而不是基于类的组件。 Functional based component costs less than class based component and can boost the performance.基于功能的组件成本低于基于类的组件,并且可以提高性能。

Below are updated code, tested.下面是更新的代码,经过测试。 I assume you use Material UI's TextField, but onChangeHandler should also work in your own component.我假设您使用 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

Again, IF you out source the onChangeHandler to App class, MetafieldInputs can be a pure functional component, and all the state management can be done in the App class.同样,如果您将onChangeHandler源出到App类, MetafieldInputs可以是一个纯功能组件,并且所有状态管理都可以在App类中完成。

On the other hand, if you want to keep a pure and clean App class, you can also store metafields into MetafieldInputs class in case you might need some other logic in your application.另一方面,如果你想保持一个纯粹和干净的App类,你也可以将元字段存储到 MetafieldInputs 类中,以防你的应用程序中可能需要一些其他逻辑。

For instance, your application renders more components than the example does, and MetafieldInputs should not be rendered until something happened.例如,您的应用程序渲染的组件比示例多,并且在发生某些事情之前不应渲染 MetafieldInputs。 If you fetch data from server end, it is better to fetch the data when it is needed rather than fetching all the data in the App component.如果从服务端取数据,最好在需要的时候取数据,而不是取App组件中的所有数据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM