Code Review React Code Snippets Sequels - herougo/SoftwareEngineerKnowledgeRepository GitHub Wiki
Follow-ups to React Code Snippets
import { ButtonHTMLAttributes, DetailedHTMLProps } from 'react';
export type NestedObject<T> = {
[k: string]: T | NestedObject<T>
}
const concatClassNames = (
classNames1: string | undefined,
classNames2: string | undefined
): string | undefined => {
if (!classNames1) {
return classNames2;
} else if (!classNames2) {
return classNames1;
} else {
return `${classNames1} ${classNames2}`;
}
};
function flatten<T>(obj: NestedObject<T>, parentKey = '', result: Record<string, T> = {}): Record<string, T> {
for (const key in obj) {
const value = obj[key];
const newKey = parentKey ? `${parentKey}_${key}` : key;
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
flatten(value as NestedObject<T>, newKey, result);
} else {
result[newKey] = value as T;
}
}
return result;
}
class TailwindClasses<T> {
constantClassName: string;
dynamicClassNameFn: (undefined | ((args: T) => string));
constructor(
constant: NestedObject<string>,
dynamic?: (undefined | ((args: T) => string))
) {
this.constantClassName = Object.values(flatten(constant)).join(' ');
this.dynamicClassNameFn = dynamic;
}
extract(args: T): string | undefined {
if (this.dynamicClassNameFn) {
return concatClassNames(this.constantClassName, this.dynamicClassNameFn(args));
}
return this.constantClassName;
}
};
const buttonTailwindClasses = new TailwindClasses<{active: boolean}>(
{
reuse: {
other: 'text-base whitespace-nowrap cursor-pointer'
},
colour: 'bg-blue-500 text-white hover:bg-blue-300 active:bg-blue-500',
border: 'border-0 rounded-full'
},
({active}: {active: boolean}) => {
const result: Record<string, string> = {};
if (active) {
result.colour = 'bg-blue-300';
}
return Object.values(result).join(' ');
}
);
export type MyButtonProps = DetailedHTMLProps<
ButtonHTMLAttributes<HTMLButtonElement>,
HTMLButtonElement
> & {active: boolean };
const BlueButton = (props: MyButtonProps) => {
const newProps = {
...props,
className: buttonTailwindClasses.extract({active: props.active})
};
return (
<button {...newProps} />
);
}Criticism
- The class approach kind of bloats the code?