Skip to content

Commit d77e080

Browse files
committed
feat: replace ComponentRef props with ComponentLookup components
1 parent 8221585 commit d77e080

File tree

3 files changed

+94
-53
lines changed

3 files changed

+94
-53
lines changed

README.md

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ npm install --save react-from-json
4343

4444
## Usage
4545

46-
### With recursion
47-
4846
```jsx
4947
import React from "react";
5048
import ReactFromJSON from "react-from-json";
@@ -77,11 +75,37 @@ const Example = () => {
7775
};
7876
```
7977

80-
### Without recursion
78+
### Flat trees
79+
80+
`react-from-json` also supports flat, non-recursive structures via the special `<ComponentLookup />` component. This is useful when working with typed systems like GraphQL, and you need to avoid unions.
81+
82+
The `<ComponentLookup />` simply maps to another component defined in a `components` object. If you were using it in React, you would use it like:
83+
84+
```jsx
85+
<ComponentLookup componentType="Button" componentIndex={0} />
86+
```
87+
88+
which would look up the `Button` component at index `0` in the `components` object, resolving to:
89+
90+
```jsx
91+
<Button>Hello, World!</Button>
92+
```
93+
94+
For `react-from-json` we use JSON, so we would write this:
95+
96+
```json
97+
{
98+
"type": "ComponentLookup",
99+
"props": {
100+
"componentType": "Button",
101+
"componentIndex": 0
102+
}
103+
}
104+
```
81105

82-
`react-from-json` also supports non-recursive structures via the special `ComponentRef` prop. This is useful when working with typed systems like GraphQL, and you need to avoid unions.
106+
#### Example
83107

84-
Here's the same example as above, instead using a `ComponentRef` for `entry.props.patty`, and providing a separate `components` object.
108+
Here's the same example as above, instead using a `<ComponentLookup />` for `entry.props.patty`, and providing a separate `components` object.
85109

86110
```jsx
87111
import React from "react";
@@ -92,8 +116,11 @@ const entry = {
92116
props: {
93117
chain: "Wahlburger",
94118
patty: {
95-
componentIndex: 0,
96-
componentType: "Patty"
119+
type: "ComponentLookup",
120+
props: {
121+
componentIndex: 0,
122+
componentType: "Patty"
123+
}
97124
}
98125
}
99126
};
@@ -126,15 +153,6 @@ const Example = () => {
126153
};
127154
```
128155

129-
The `ComponentRef` prop looks like:
130-
131-
```ts
132-
interface ComponentRef {
133-
componentIndex: number;
134-
componentType: string;
135-
}
136-
```
137-
138156
## With TypeScript
139157

140158
`react-from-json` supports generic types for use with TypeScript.

src/__helpers__/data.ts

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,27 @@ export const flatEntry = {
4646
props: {
4747
chain: "Wahlburger",
4848
bun: {
49-
componentType: "Bun",
50-
componentIndex: 0
49+
type: "ComponentLookup",
50+
props: {
51+
componentType: "Bun",
52+
componentIndex: 0
53+
}
5154
},
5255
cheese: true,
5356
children: [
5457
{
55-
componentType: "Patty",
56-
componentIndex: 0
58+
type: "ComponentLookup",
59+
props: {
60+
componentType: "Patty",
61+
componentIndex: 0
62+
}
5763
},
5864
{
59-
componentType: "Patty",
60-
componentIndex: 1
65+
type: "ComponentLookup",
66+
props: {
67+
componentType: "Patty",
68+
componentIndex: 1
69+
}
6170
}
6271
]
6372
}
@@ -78,8 +87,11 @@ export const flatComponents: Components = {
7887
props: {
7988
size: "large",
8089
ingredient: {
81-
componentType: "PattyIngredient",
82-
componentIndex: 0
90+
type: "ComponentLookup",
91+
props: {
92+
componentType: "PattyIngredient",
93+
componentIndex: 0
94+
}
8395
}
8496
}
8597
},
@@ -88,8 +100,11 @@ export const flatComponents: Components = {
88100
props: {
89101
size: "large",
90102
ingredient: {
91-
componentType: "PattyIngredient",
92-
componentIndex: 1
103+
type: "ComponentLookup",
104+
props: {
105+
componentType: "PattyIngredient",
106+
componentIndex: 1
107+
}
93108
}
94109
}
95110
}

src/index.tsx

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,19 @@ export interface Component {
55
props: object;
66
}
77

8-
export interface ComponentRef {
8+
interface ComponentLookupProps {
99
componentType: string;
1010
componentIndex: number;
1111
}
1212

13-
export interface ReactFromJSONProps<MappingType, ComponentsType> {
13+
export interface ComponentLookup {
14+
props: ComponentLookupProps;
15+
}
16+
17+
export interface ReactFromJSONProps<
18+
MappingType = object,
19+
ComponentsType = object
20+
> {
1421
components?: ComponentsType;
1522
entry: Component;
1623
mapping: MappingType;
@@ -25,22 +32,36 @@ class ReactFromJSON<
2532
> extends React.Component<ReactFromJSONProps<MappingType, ComponentsType>> {
2633
public counter = {};
2734

35+
public internalMapping: object = {};
36+
37+
constructor(props: any) {
38+
super(props);
39+
40+
this.internalMapping = {
41+
ComponentLookup: this.ComponentLookup
42+
};
43+
}
44+
45+
ComponentLookup = ({
46+
componentIndex,
47+
componentType
48+
}: ComponentLookupProps) => {
49+
const { components } = this.props;
50+
51+
if (!components) {
52+
throw "Detected `ComponentLookup` prop on a component, but `components` is undefined. You need to define `components` if using `ComponentLookup` props.";
53+
}
54+
55+
const component = components[componentType][componentIndex];
56+
57+
return this.renderComponent(component);
58+
};
59+
2860
resolveProp = (prop: any): any => {
2961
if (Array.isArray(prop)) {
3062
return prop.map(this.resolveProp);
3163
} else if (typeof prop === "object") {
32-
// Handle componentRef
3364
if (
34-
// Typeguard
35-
prop["componentType"] !== undefined &&
36-
prop["componentIndex"] !== undefined
37-
) {
38-
const componentRef: ComponentRef = prop;
39-
40-
return this.componentLookup(componentRef);
41-
42-
// Handle nested components
43-
} else if (
4465
// Typeguard
4566
prop["type"] !== undefined &&
4667
prop["props"] !== undefined
@@ -54,19 +75,6 @@ class ReactFromJSON<
5475
return prop;
5576
};
5677

57-
componentLookup = (componentRef: ComponentRef) => {
58-
const { components } = this.props;
59-
const { componentIndex, componentType } = componentRef;
60-
61-
if (!components) {
62-
throw "Detected `ComponentRef` prop on a component, but `components` is undefined. You need to define `components` if using `ComponentRef` props.";
63-
}
64-
65-
const component = components[componentType][componentIndex];
66-
67-
return this.renderComponent(component);
68-
};
69-
7078
getNextKey(type: string) {
7179
this.counter[type] = this.counter[type] || 0;
7280
return `${type}_${this.counter[type]++}`;
@@ -87,7 +95,7 @@ class ReactFromJSON<
8795
resolvedProps[propKey] = this.resolveProp(prop);
8896
}
8997

90-
const MappedComponent = mapping[type];
98+
const MappedComponent = this.internalMapping[type] || mapping[type];
9199

92100
if (typeof MappedComponent === "undefined") {
93101
throw `Tried to render the "${type}" component, but it's not specified in your mapping.`;

0 commit comments

Comments
 (0)