Is there a way to both destructure a function parameter, and keep a named reference to the parameter?
In react stateless functional components we'll commonly write something like this:
export function MyCompoment({
title,
foo,
bar
}) {
return <div> title: {title}, ...</div>
}
Where we're immediately destructuring the props object into its variables.
Now I'm using a Material-UI makeStyles
hook, and I'm also using TypeScript, and the way I'm currently using it looks like this.
const useStyles = makeStyles((theme : Theme ) =>({
root: ({foo} : MyComponentProps) => ({
content: foo
})
});
interface MyComponentProps {
title: string;
foo: string;
bar: string;
}
export function MyCompoment({
title,
foo,
bar
} : MyComponentProps) {
const classes = useStyles({
title,
foo,
bar
});
return <div> title: {title}, ...</div>
}
And you can see the problem - I have to repeat the props variable names to pass into the classes.
The best way I can think to avoid this, is write it this way:
export function MyCompoment(props: MyComponentProps) {
const {
title,
foo,
bar
} = props;
const classes = useStyles(props);
return <div> title: {title}, ...</div>
}
But that's a little messier than I was wanting.
I was wondering if it's possible to do something like:
export function MyCompoment({
title,
foo,
bar
} = props : MyComponentProps) {
const classes = useStyles(props);
return <div> title: {title}, ...</div>
}
Pretty picky I know, just wondering.
Answers
In TypeScript with React functional components, especially when using Material-UI's makeStyles
hook, you have a couple of options to streamline your component code while avoiding unnecessary repetition of prop names.
Let's explore a couple of approaches:
Approach 1: Destructuring Props and Passing to makeStyles
You can destructure the props immediately in the function arguments and then use them directly in the makeStyles
hook:
import React from 'react';
import { makeStyles, Theme } from '@material-ui/core/styles';
interface MyComponentProps {
title: string;
foo: string;
bar: string;
}
const useStyles = makeStyles((theme: Theme) => ({
root: ({ foo }: MyComponentProps) => ({
content: foo,
}),
}));
export function MyComponent({ title, foo, bar }: MyComponentProps) {
const classes = useStyles({ foo }); // Pass only necessary props
return <div>title: {title}, ...</div>;
}
- Explanation:
- Props Destructuring: Destructure
title
,foo
, andbar
directly fromMyComponentProps
. - Passing to
makeStyles
: Pass only the necessary props (foo
in this case) tomakeStyles
. This avoids passing unnecessary props and keeps the hook concise.
- Props Destructuring: Destructure
Approach 2: Using Spread Operator with Props
If you prefer to keep your props intact and pass them all to makeStyles
, you can use the spread operator (...props
) to achieve this:
import React from 'react';
import { makeStyles, Theme } from '@material-ui/core/styles';
interface MyComponentProps {
title: string;
foo: string;
bar: string;
}
const useStyles = makeStyles((theme: Theme) => ({
root: (props: MyComponentProps) => ({
content: props.foo,
}),
}));
export function MyComponent(props: MyComponentProps) {
const classes = useStyles(props); // Pass all props to useStyles
return <div>title: {props.title}, ...</div>;
}
- Explanation:
- Passing All Props: Use
useStyles(props)
to pass all props (title
,foo
,bar
) tomakeStyles
. - Accessing Specific Props: Inside
makeStyles
, access the specific prop (foo
in this case) asprops.foo
.
- Passing All Props: Use
Conclusion
Both approaches are valid and can be chosen based on personal preference and specific requirements of your application. The second approach keeps your props intact and might be preferable if you need to access multiple props inside makeStyles
. The first approach minimizes the props passed to makeStyles
, potentially improving performance and reducing unnecessary re-renders.
Choose the approach that fits best with your component structure and readability preferences. Each approach ensures type safety and clarity in your TypeScript codebase.