简体   繁体   English

当使用 cloneElement 在组件 props.children 上传递方法时,React,Typescript,递归映射中出现“.map 不是函数”错误

[英]React,Typescript, ".map is not a function" error in recursive map when using cloneElement to pass a method on component props.children

I am a react newbie, and trying to write a CustomFormComp that will be used like below, I will have some form element components too, which will be used like InputComponent .我是一个反应新手,并试图编写一个将像下面这样使用的CustomFormComp ,我也会有一些表单元素组件,它们将像InputComponent一样使用
The child Components ( InputComponent ) need to get a method from the parent component( CustomFormComp ) to handle onChnage and lift up their state to CustomFormComp , although they should be able to combine onChange with a given method to them as callbacks.子组件 ( InputComponent ) 需要从父组件 ( CustomFormComp ) 获取一个方法来处理onChnage并将它们的状态提升到CustomFormComp ,尽管它们应该能够将onChange与给定的方法结合起来作为回调。
And also if inner components bind correctly I should be able to pass them some data.而且如果内部组件正确绑定,我应该能够向它们传递一些数据。

import React, { Component } from "react";
import CustomFormComp from "../Form";
import InputComponent from "../FormElements/InputComponent";

interface IState { }

interface IProps { }

interface FormElData {
  [key: string]: string;
}

export class CustomForm extends Component<IProps, IState> {
  render() {
    const formData: FormElData = {
      email: "someone@some-mail.com",
      phone: "sdfsdf",
      address: "asdhf akshdfkahsdkahsdflka hsfnasdk f"
    };

    return (
      <CustomFormComp FormData={formData}>
        <h2>f</h2>
        asd
        <h3>asdf</h3>
        <div className="c1">
          <div className="c2">
            <div className="c3">
              <InputComponent label="email: " name="email" />
            </div>
            <div className="c33"></div>
          </div>
          <div className="c22"></div>
        </div>
        <InputComponent label="phone: " name="phone" />
      </CustomFormComp>
    );
  }
}

So far this is my customFromComp :到目前为止,这是我的customFromComp

import React, { Component, ReactNode, Fragment } from "react"

interface FormElData {
    [key: string]: string
}

interface IState {
    FormData: {
        [key: string]: string
    }
}

type ICustomFormChildren = Array<null | React.ReactChild> //  | React.ReactChildren

interface IProps {
    FormData: FormElData
    children: ICustomFormChildren
}

class CustomFormComp extends Component<IProps, IState> {
    state = {
        FormData: {}
    }

    constructor(props: IProps) {
        super(props)
        this.handleInputChange = this.handleInputChange.bind(this)
        this.handleSubmit = this.handleSubmit.bind(this)
        this.state.FormData = this.props.FormData
    }

    componentDidMount() {
        this.setState({
            FormData: this.props.FormData
        })
    }

    handleInputChange(inputData: FormElData) {
        const newFormData: any = { ...this.state.FormData }
        newFormData[inputData.name] = inputData.value

        this.setState({
            FormData: newFormData
        })
    }

    handleSubmit(event: React.FormEvent) {
        event.preventDefault()
        console.log(this.state)
    }

    recursiveClone(children: ICustomFormChildren): ICustomFormChildren {
        return (children).map((child, index: number) => {
            let value: string

            if (React.isValidElement(child)) {
                if (
                    child.props &&
                    this.state.FormData &&
                    (this.state.FormData as any)[child.props.name]) {
                    value = (this.state.FormData as any)[child.props.name]

                    return React.cloneElement(child, {
                        onChange: this.handleInputChange.bind(this),
                        key: index,
                        value: value
                    })
                } else if (!child.props || !Object.keys(child.props).length) {
                    return <Fragment key={index}>{child}</Fragment>
                } else {
                    console.log(Array.isArray(child.props.children), child.props.children)
                    return React.cloneElement(
                        child,
                        { key: index },
                        this.recursiveClone(child.props.children)
                    )
                }
            } else {
                return <Fragment key={index}>{child}</Fragment>
            }
        })
    }

    render() {
        const newChildren = this.recursiveClone(this.props.children)

        return (
            <form style={{ direction: "ltr" }} onSubmit={this.handleSubmit}>
                {newChildren}
            </form>
        )
    }
}

export default CustomFormComp

and this is my InputComponent :这是我的InputComponent

import React, { Component } from 'react'

interface inputData {
  name: string,
  label: string,
  value?: string,
  onChange?(e:any): void
}

class InputComponent extends React.Component<inputData> {
  constructor(props:inputData) {
    super(props)
    this.onChange = this.onChange.bind(this)
  }

  onChange(e:any) {
    this.props.onChange && this.props.onChange({
      name: this.props.name,
      value: e.target.value
    })
  }

  render() {
    const value:string | undefined = this.props.value
    const name:string = this.props.name

    return (
      <fieldset>
        <label>
          {this.props.label}
          <input name={name} value={value} onChange={this.onChange} />
        </label>
      </fieldset>
    )
  }
}

export default InputComponent

Now I think It should work but some types error prevent page from loading, this is the error:现在我认为它应该可以工作,但某些类型的错误会阻止页面加载,这是错误:
在此处输入图片说明

**UPDATE**

Main problem here is this:这里的主要问题是:
The first time that i run the map it works fine, when I pass the child.props.children to in recursion it does not recognize the new argument(children which is child.props.children) and it trow an error.我第一次运行地图时,它工作正常,当我将child.props.children传递给递归时,它无法识别新参数(即 child.props.children 的 children)并抛出错误。

The problem is that children in react are not always an array, if you have only one child it is not an array.问题是反应中的孩子并不总是一个数组,如果你只有一个孩子它不是一个数组。

Your code will work if you force recursive clone to send an array when there is only one child.如果您强制递归克隆在只有一个孩子时发送数组,您的代码将起作用。 See here:看这里:

recursiveClone(children: ICustomFormChildren): ICustomFormChildren {
    return children.map((child, index: number) => {
      let value: string;

      if (React.isValidElement(child)) {
        if (
          child.props &&
          this.state.FormData &&
          (this.state.FormData as any)[child.props.name]
        ) {
          value = (this.state.FormData as any)[child.props.name];

          return React.cloneElement(child, {
            onChange: this.handleInputChange.bind(this),
            key: index,
            value: value
          });
        } else if (!child.props || !Object.keys(child.props).length) {
          return <Fragment key={index}>{child}</Fragment>;
        } else {
          console.log(
            Array.isArray(child.props.children),
            child.props.children
          );
          if (!Array.isArray(child.props.children)) {
            return React.cloneElement(
              child,
              { key: index },
              this.recursiveClone([child.props.children])
            );
          }
          return React.cloneElement(
            child,
            { key: index },
            this.recursiveClone(child.props.children)
          );
        }
      } else {
        return <Fragment key={index}>{child}</Fragment>;
      }
    });
  }

I added this if in your else如果在你的其他人中,我添加了这个

if (!Array.isArray(child.props.children)) {
            return React.cloneElement(
              child,
              { key: index },
              this.recursiveClone([child.props.children])
            );
          }

I tested it here in this https://codesandbox.io/s/new-surf-d17rk我在这里测试了这个https://codesandbox.io/s/new-surf-d17rk

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

相关问题 使用react props.children(i)映射方法生成嵌套循环组件时,组件的数量和预期的不匹配 - When the nested loop component is generated using the react props.children (i) map method, the number of components and the expected mismatch React.cloneElement:传递新的孩子或复制props.children? - React.cloneElement: pass new children or copy props.children? 带有 props.children 的 Typescript React 功能组件 - Typescript React Functional Component with props.children 使用 this.props.children() 和 React.cloneElement 进行渲染时,如何将 Parent 道具传递给 Children? - How to pass Parent props to Children, when rendering using this.props.children() and React.cloneElement? React/TypeScript:专门为组件输入 props.children - React/TypeScript: Specifically type props.children to component 使用带有 typescript 的 props.children 和 styled-component "as" 道具 - Using props.children and styled-component "as" prop with typescript ButtonGroup 组件使用 props.children 使用 mui 反应 - ButtonGroup component using props.children using mui react 在 Typescript/react 中访问 props.children - Access props.children in Typescript/react 如何在反应 typescript 中限制 props.children? - How to restrict props.children in react typescript? 无法使用React.cloneElement传递带有道具的孩子? - Can't pass children with props using React.cloneElement?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM