我想要一个可以帮助我更轻松地将数据传递给子组件的组件。 这是一个用例:
const Test = () => {
const data = {
loginForm: {
name: "Batman",
password: "hard password"
},
email: "test@gmail.com"
};
return (
<Block data={data}>
<div>
<TextBox name="email" />
</div>
<form>
<Block name="loginForm">
<div>
<TextBox name="name" />
</div>
<div>
<TextBox name="password" />
</div>
</Block>
</form>
</Block>
);
};
TextBox是:
const TextBox = ({ name, data }) => {
return (
<div>
<label>{name}</label>
<input value={data} />
</div>
);
};
Block是我想要的组件。名为='email'的TextBox将接收data = test@gmail.com 。请注意,它根据子组件的 name 属性传递数据。 secord Block将接收data =
{
name: "Batman",
password: "hard password"
}
这就是我所做的:
const Block = ({ name, data, children }) => {
return passDataToChildren(children, data)
}
function passDataToChildren(children, parentData) {
return React.Children.map(children, (child) => {
if (typeof(child.type) !== "function") {
return React.cloneElement(child, {
children: passDataToChildren(child.props.children, parentData)
});
}
if (!child.props.name) {
return child;
}
return React.cloneElement(child, {
data: _.get(parentData, child.props.name),
});
});
}
有没有其他方法可以实现相同的行为?我可以使用新的上下文API来实现这个吗?
修改
我想要这种行为,因为除了 data 之外,Block还会传递 errors ,这是一个对象,其中包含数据中每个属性的消息数组和更新函数每个孩子。
function passDataToChildren(children, parentProps) {
return React.Children.map(children, (child) => {
if (typeof(child.type) !== "function") {
return React.cloneElement(child, {
children: passDataToChildren(child.props.children, parentProps)
});
}
if (!child.props.name) {
return child;
}
return React.cloneElement(child, {
data: _.get(parentProps.data, child.props.name),
errors: _.get(parentProps.errors, child.props.name),
onChange: (value, name = child.props.name) => {
if (parentProps.onChange) {
parentProps.onChange(value, parentProps.name + "." + name);
}
}
});
});
}
所以TextBox是:
const TextBox = ({ name, data, onChange, errors }) => {
return (
<div>
<label>{name}</label>
<input value={data} onChange={onChange} />
<span>{errors.toString()}</span>
</div>
);
};
我还有另一个组件 FormViewer ,其中包含道具:数据,错误, onChange 和表单:
const form = {
["0, 0"]: {
name: "email",
renderWith: "TextBox",
},
["1, 0"]: {
name: "loginForm.name",
renderWith: "TextBox",
},
["2, 0"]: {
name: "loginForm",
renderWith: {
["0, 0"]: {
renderWith: "TextBox",
name: "password",
props: { type="password" }
}
}
}
}
FormViewer 将使用 Block 并构建基于 form 属性的接口