是否可以创建 children 节点来为 parent 组件提供配置?
Is it possible to create children nodes that provide configuration to the parent component?
我想要执行此操作的场景是创建一个通用数据网格组件。我想使用 children 来定义网格的列,但我不希望那些 children 呈现,因为呈现逻辑被抽象为网格结构的表示(视图 属性 下面)
即
<MyDataGrid data={myData} view={myTableView}>
<MyCol fieldName='asdf' sortable />
</MyDataGrid>
因为列向网格提供渲染信息,所以它们需要在网格的渲染函数中访问,而无需首先渲染它们自己。据我所知,这是不可能的,我目前只是将列配置作为网格上的道具传递。这个策略效果很好,但肯定不是最好看的策略。
我知道你可以构建可以使用的内在元素,但我认为 React 仍然希望将它们作为 DOM 节点来管理。我想要的是忽略我组件的 children 的 DOM,让我逐字解析 children(即作为 MyCol { fieldName: string, sortable: boolean }
的数组)。
这可能吗?计划在路线图上?甚至考虑过?
我知道这是一个有点奇怪的问题,我很高兴继续我目前采用的策略。但是如果可以选择创建这些无渲染的“dumb”节点会很好。
好的!查看 React Router 以获取此类配置的示例。也就是说,我认为它更适合嵌套的东西(比如路由配置);否则,我建议只使用更 JavaScript 为中心的定义对象或对象数组的样式来配置网格。
在您的示例中,MyCol
不需要 呈现 ,您只想检查创建它时使用的属性。这是一个例子:
var MyDataGrid = React.createClass({
render: function() {
var options = React.Children.map(this.props.children, (child) => {
return {
type: child.type.displayName,
sortable: !!child.props.sortable,
fieldName: child.props.fieldName
};
});
return <pre>{JSON.stringify(options, null, " ")}</pre>
}
});
var MyCol = React.createClass({
render: function() {
return null;
}
});
var app = (
<MyDataGrid>
<MyCol fieldName='asdf' sortable />
<MyCol fieldName='anotherColumn' />
</MyDataGrid>
);
ReactDOM.render(app, document.getElementById("app"));
为配置处理子组件的主要问题是,当您在父组件中接收到子组件时,您的 React 组件 class 不会调用它的构造函数。如果您有任何与需要应用的配置相关的逻辑 node/component,这会使事情变得复杂。
我现在使用的模式如下(已确认可以正常工作)。我正在为组件使用 ES6 classes,但该模式也适用于函数式 React.create*
样式。
- 创建道具接口(这定义了哪些道具可以在您的配置子节点中使用)
- 创建一个虚拟组件 class 来使用您创建的道具(这个组件实际上只是一个定义,不包含任何代码)
- 使用构造函数创建一个配置 class,该构造函数使用所有 props 并执行任何初始化逻辑。
- 向您的配置 class 添加一个静态
create
函数,该函数使用 ReactElement
和 returns 您的配置的新实例 class(元素将具有 jsx/tsx 中定义的道具
- 使用
React.Children.map
将子道具转换为父级中的配置实例数组(这可以在 componentWillMount
中完成并保存到实例变量中,除非您的列定义是可变的)。
简化示例(Typescript)
interface IDataGridColumnProps {
fieldName: string;
header?: string;
sortable?: boolean;
}
export class DataGridColumn extends React.Component<IDataGridColumnProps, any> {}
class Column {
constructor(public fieldName: string, public header?: string, public sortable = false) {
if (this.header == null) {
this.header = this.fieldName;
}
}
public static create(element: React.ReactElement<IDataGridColumnProps>) {
return new Column(element.props.fieldName, element.props.header, element.props.sortable);
}
}
export class DataGridView /* ... */ {
private columns: Column[];
componentWillMount() {
this.columns = React.Children.map(this.props.children, (x: React.ReactElement<IDataGridColumnProps>) => {
return Column.create(x);
});
}
}
上面使用的模式本质上是将组件节点转换为父组件上的配置实例。原始子节点被丢弃,它们提供的配置保留在父组件内的实例数组中。
我想要执行此操作的场景是创建一个通用数据网格组件。我想使用 children 来定义网格的列,但我不希望那些 children 呈现,因为呈现逻辑被抽象为网格结构的表示(视图 属性 下面)
即
<MyDataGrid data={myData} view={myTableView}>
<MyCol fieldName='asdf' sortable />
</MyDataGrid>
因为列向网格提供渲染信息,所以它们需要在网格的渲染函数中访问,而无需首先渲染它们自己。据我所知,这是不可能的,我目前只是将列配置作为网格上的道具传递。这个策略效果很好,但肯定不是最好看的策略。
我知道你可以构建可以使用的内在元素,但我认为 React 仍然希望将它们作为 DOM 节点来管理。我想要的是忽略我组件的 children 的 DOM,让我逐字解析 children(即作为 MyCol { fieldName: string, sortable: boolean }
的数组)。
这可能吗?计划在路线图上?甚至考虑过?
我知道这是一个有点奇怪的问题,我很高兴继续我目前采用的策略。但是如果可以选择创建这些无渲染的“dumb”节点会很好。
好的!查看 React Router 以获取此类配置的示例。也就是说,我认为它更适合嵌套的东西(比如路由配置);否则,我建议只使用更 JavaScript 为中心的定义对象或对象数组的样式来配置网格。
在您的示例中,MyCol
不需要 呈现 ,您只想检查创建它时使用的属性。这是一个例子:
var MyDataGrid = React.createClass({
render: function() {
var options = React.Children.map(this.props.children, (child) => {
return {
type: child.type.displayName,
sortable: !!child.props.sortable,
fieldName: child.props.fieldName
};
});
return <pre>{JSON.stringify(options, null, " ")}</pre>
}
});
var MyCol = React.createClass({
render: function() {
return null;
}
});
var app = (
<MyDataGrid>
<MyCol fieldName='asdf' sortable />
<MyCol fieldName='anotherColumn' />
</MyDataGrid>
);
ReactDOM.render(app, document.getElementById("app"));
为配置处理子组件的主要问题是,当您在父组件中接收到子组件时,您的 React 组件 class 不会调用它的构造函数。如果您有任何与需要应用的配置相关的逻辑 node/component,这会使事情变得复杂。
我现在使用的模式如下(已确认可以正常工作)。我正在为组件使用 ES6 classes,但该模式也适用于函数式 React.create*
样式。
- 创建道具接口(这定义了哪些道具可以在您的配置子节点中使用)
- 创建一个虚拟组件 class 来使用您创建的道具(这个组件实际上只是一个定义,不包含任何代码)
- 使用构造函数创建一个配置 class,该构造函数使用所有 props 并执行任何初始化逻辑。
- 向您的配置 class 添加一个静态
create
函数,该函数使用ReactElement
和 returns 您的配置的新实例 class(元素将具有 jsx/tsx 中定义的道具
- 使用
React.Children.map
将子道具转换为父级中的配置实例数组(这可以在componentWillMount
中完成并保存到实例变量中,除非您的列定义是可变的)。
简化示例(Typescript)
interface IDataGridColumnProps {
fieldName: string;
header?: string;
sortable?: boolean;
}
export class DataGridColumn extends React.Component<IDataGridColumnProps, any> {}
class Column {
constructor(public fieldName: string, public header?: string, public sortable = false) {
if (this.header == null) {
this.header = this.fieldName;
}
}
public static create(element: React.ReactElement<IDataGridColumnProps>) {
return new Column(element.props.fieldName, element.props.header, element.props.sortable);
}
}
export class DataGridView /* ... */ {
private columns: Column[];
componentWillMount() {
this.columns = React.Children.map(this.props.children, (x: React.ReactElement<IDataGridColumnProps>) => {
return Column.create(x);
});
}
}
上面使用的模式本质上是将组件节点转换为父组件上的配置实例。原始子节点被丢弃,它们提供的配置保留在父组件内的实例数组中。