How can I select an object key in a theme object using a string used in react component prop?
theme
{
palette: {
primary: {
main: '#dc2626',
dark: '#7f1d1d',
light: '#fecaca',
}
text: '#111827',
}
}
component
const Typography = ({ color, children }) => (
<p style={{ color: theme.palette[color] }}>{children}</p>
);
How I want to use the component:
<Typography color='primary.main'>Hello world</Typography>
Let me know if this is a bad practice and should be avoided anyway.
I tried to use the eval function but found out it was not secure and considered to be a bad practice.
CodePudding user response:
If you are just looking how to get a nested field in an object, you should look at lodash's get function.
Example :
export default const theme = {
palette: {
primary: {
main: '#dc2626',
dark: '#7f1d1d',
light: '#fecaca',
}
text: '#111827',
}
};
Component
import { get } from lodash library;
import theme from './theme.js';
export default function MyComponent({ children }) {
const nestedFieldPath = 'palette.primary.main';
// Get nested field value
const nestedFieldValue = get(theme, nestedFieldPath);
return <p color={nestedFieldValue}>{children}</p>
}
CodePudding user response:
You could make a hook that can be extended as well as handle a default incase no color is being passed to your typography component
Example hook:
import React from "react";
const useColor = (color = null) => {
// Set the default theme
const [selectedTheme, setSelectedTheme] = React.useState(["primary", "main"]);
React.useEffect(() => {
if (color) {
setSelectedTheme(color.split("."));
}
}, [color]);
return selectedTheme;
};
export default useColor;
Then the Typography component you're returning an array from the hook so deconstruct it into 2 values. Naming is of course questionable here :
const Typography = ({ color, children }) => {
const [level, choice] = useColor(color);
return <p style={{ color: theme.palette[level][choice] }}>{children}</p>;
};
Usage:
<Typography color={"primary.main"}>Hello</Typography>
A codesandbox : https://codesandbox.io/s/clever-julien-rr0tuf?file=/src/App.js:506-561
CodePudding user response:
This would be my solution, without using any additional libraries.
Idea is to create a function with method for getting the nested color value from your Theme object and then use that function (getColor) in your components.
This can handle additional levels of nesting.
Bare in mind, my experience with React is reading 3 pages of docs. It is not clear from question how you want to handle situation when you try to get color that is not in theme, you would have to handle that inside reduce.
function Theme () {
let palette = {
primary: {
main: '#dc2626',
dark: '#7f1d1d',
light: '#fecaca',
},
secondary : {
green: {
lightgreen : 'myfancycolor :)'
}
}
}
this.getColor = function(string){
let nesting = string.split(".");
const result = nesting.reduce(
(accumulator, currentValue) => {
if (accumulator[currentValue])
return accumulator[currentValue];
else
return accumulator
} ,
palette
);
return result;
}
}
let myTheme = new Theme();
console.log(myTheme.getColor("secondary.green.lightgreen"))
//prints myfancycolor :)
Usage in component:
const Typography = ({ color, children }) => (
<p style={{ color: myTheme.getColor("primary.dark") }}>{children}</p>
);