react-codemod
react-codemod copied to clipboard
Render Prop Expression to Child Component Transformer code
I have working code to make the render prop expression as a child component.
Please let me know if this transformer code would be useful to add to the react-codemod code base.
My use case was that the Formik library is deprecating the use of render prop in their API. https://formik.org/docs/api/formik#render-props-formikpropsvalues--reactnode
https://astexplorer.net/#/gist/d3df017f80a840c55c4f7041154c0a0d/8a482bb109f7c5e34fc8ab43296f797991d1989b Downloadable file: https://gist.github.com/d3df017f80a840c55c4f7041154c0a0d/8a482bb109f7c5e34fc8ab43296f797991d1989b
Here is the transformer code:
const isRenderProp = (object) => {
return object.name && object.name.name == "render";
};
const hasRenderProp = (path) => {
return path && path.value && path.value.openingElement && path.value.openingElement.attributes.filter(isRenderProp).length > 0;
};
const update = (api) => (path) => {
const renderProp = path.value.openingElement.attributes.filter(isRenderProp)[0];
// remove render prop
path.value.openingElement.attributes = path.value.openingElement.attributes.filter((path) => !isRenderProp(path));
// make field a non closing field
path.value.openingElement.selfClosing = false;
path.value.closingElement = api.jscodeshift.jsxClosingElement(path.value.openingElement.name);
// make element in render prop now a child of the react element
if (renderProp && renderProp.value && renderProp.value.expression) {
path.value.children.push(renderProp.value);
}
return path;
};
export default function transformer(file, api) {
const j = api.jscodeshift;
const root = j(file.source);
root
.find(j.JSXElement, {
closingElement: null
})
.filter(hasRenderProp)
.forEach(update(api));
return root.toSource();
}
Example Input:
const App = () => {
<FieldArray
name={orderLinesFieldName}
render={(arrayHelpers) => {
const priceFieldName = `${orderLinesFieldName}[${i}].${priceFieldConst}`;
return (
<Field
name={priceFieldName}
render={({ form, field }) => {
const error = getIn(form.errors, field.name);
return (
<CenteredCell>
<Block>USD</Block>
<Input
{...(error ? { error: true } : {})}
type="number"
overrides={{
Input: {
props: {
"data-testid": "existing-adjustment-amount",
},
},
}}
disabled={actionItemStatus === statusClosed}
value={orderLines[i].price}
onChange={(e) => {
arrayHelpers.replace(i, {
...orderLines[i],
price: e.target.value,
});
}}
/>
</CenteredCell>
);
}}
/>
);
Example output
const App = () => {
<FieldArray name={orderLinesFieldName}>{(arrayHelpers) => {
const priceFieldName = `${orderLinesFieldName}[${i}].${priceFieldConst}`;
return (
(<Field name={priceFieldName}>{({ form, field }) => {
const error = getIn(form.errors, field.name);
return (
<CenteredCell>
<Block>USD</Block>
<Input
{...(error ? { error: true } : {})}
type="number"
overrides={{
Input: {
props: {
"data-testid": "existing-adjustment-amount",
},
},
}}
disabled={actionItemStatus === statusClosed}
value={orderLines[i].price}
onChange={(e) => {
arrayHelpers.replace(i, {
...orderLines[i],
price: e.target.value,
});
}}
/>
</CenteredCell>
);
}}</Field>)
);
}}</FieldArray>;
};
More about render props here: https://reactjs.org/docs/render-props.html