[英]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.