In an older class I have the following;
class ComponentItem extends React.PureComponent {
render() {
const { item, ...other } = this.props;
return (
<>...</>
)
}
};
Now I want to refactor and use hooks, but how can I access the named properties, and the "other properties inside a hook?
I tried
const ComponentItem = (props) => {
const classes = useStyles();
const { item, ...other } = props;
return (
<>...</>
)
}
const ComponentItem = (item, ...other) => {
const classes = useStyles();
console.log(item); // takes all passed in props
console.log(...other);
return (
<>...</>
)
}
What is the best way to do this in a function using hooks?
Probably using the rest parameters, like the following:
const ComponentItem = ({item, ...others}) => {
return <>
{ /* return of component */ }
</>
}
Read further here: Rest parameters
I hope this helps!
Wrap the component with React.memo() (the equivalent of PureComponent), and destructure the components props in params:
const ComponentItem = React.memo(({ item, ...other }) => {
const classes = useStyles();
return (
<>...</>
)
})
Related
Hey I use bootstrap with React and I try figure out, how I can extend my component by passing className props deeper. In my atom component I have two files. First one with component declaration.
Breadcrumb.js
export const Breadcrumb = (props) => {
const { className } = props;
const classes = getClasses(className);
return (
<Link to={props.path} className={classes} {...props}>
{props.children}
</Link>
);
};
and another one with getClasses() which returns all default BS classes.
Breadcrumb.style.js
export const getClasses = (extra = "") => {
const defaultClasses = getDefaultClasses();
const addingClasses = extra;
const classes = `${defaultClasses} ${addingClasses}`;
return classes;
};
const getDefaultClasses = () => `ps-3 fs-3 fw-bold text-decoration-none`;
What I want to achieve is, when I'll invoke my Breadcrumb component, and I'll decied to extend it on extra classes I can do that by pass className props...like
TopBar.js
export const TopBar = () => {
const breadcrumbs = useBreadcrumbs(routes, { disableDefaults: true });
const classes = getClasses();
return (
<div className={classes}>
{breadcrumbs.map(({ match, breadcrumb }) => (
<Breadcrumb
path={match.pathname}
children={breadcrumb}
className="cs_breadcrumb"
key={uuidv4()}
/>
))}
</div>
);
};
But when I do that, my declare Breadcrumb className is override by invoke Breadcrumb className... Although in Breadcrumb.js console.log(classes) returns concated classes.
Anyone knows how to achieve that or has any tips ?? I'll be glad
Change
export const Breadcrumb = (props) => {
const { className } = props;
const classes = getClasses(className);
return (
<Link to={props.path} className={classes} {...props}>
{props.children}
</Link>
);
};
to
export const Breadcrumb = ({ className, ...rest }) => {
const classes = getClasses(className);
return (
<Link to={props.path} className={classes} {...rest}>
{props.children}
</Link>
);
};
So, you need to extract the className prop in the place where props was, and also add ...rest for the rest props.
I guess you want to extend component classes with other classes passed via props.
If I understand correctly, you can try like this:
export const Breadcrumb = (props) => {
const { className } = props;
const classes = getClasses(className);
return (
<Link to={props.path} className={[classes, className].join(" ")]}
{...props}>
{props.children}
</Link>
);
};
I have 2 components that pass to the child the prop data, but only one parent pass the prop numPage and the prop setNumPage,
When I try to use the optional sign in the interface it tells me that React.Dispatch<React.SetStateAction> cannot be undefined. I found a solution that is inadequate, use any, can you give me another solution?
First parent
const Home = () => {
const [searchParams] = useSearchParams()
const [numPages, setNumPages] = useState<number>(1)
const url:string = searchParams.get("search") ? `${SEARCH_URL}${searchParams.get("search")}${PAGES}${numPages}` : `${POPULAR_RESULTS}${numPages}`;
const {data, loading} = useFetch<movieApi>(url);
if(loading) return <Sppiner/>
return (
<div>
<Items
data={data}
numPages={numPages}
setNumPages={setNumPages}
/>
</div>
);
};
Second parent
const GenresPage = () => {
const { data, loading } = useFetch<movieApi>(POPULAR_RESULTS);
if(loading) return <Sppiner/>
return (
<div>
<Items data={data} />
</div>
);
};
export default GenresPage;
Child
interface DataProps {
data: movieApi | null;
numPages:number;
setNumPages:React.Dispatch<React.SetStateAction<number>>;
}
const Items = ({ data,numPages,setNumPages }:DataProps) => {}
To the child if I put any or DataProps it works, but I don't wanna do that.
You can change setNumPages inside DataProps to:
setNumPages: (value: number) => void
I've found myself needing to retrieve the element ref for every parent component that my hook, useExample, is used in. However, I'm stumped as to how I might be able to retrieve something like this or how to even check if there is an element to target?
Usually I would just do something a little "hacky" in a functional component like so:
const Example = WrappedComponent => {
const ref = createRef();
return <WrappedComponent ref={ref} />;
};
However, due to it being a hook and returning information and not a component, I can't target any component, and thus I'm very stumped.
My current code:
const useExample = () => {
const [stateValue, setStateValue] = useState("example");
useEffect(() => {
// Run some code...
}, []);
return stateValue;
};
const Component = () => {
const data = useExample();
return (
<div> /* <--- How do I gain access to this element */
<span>{ data }</span>
</div>
);
};
I could probably pass a created ref which has been attached to the parent div as a parameter to useExample, however this feels cheap and hacky, and I feel there should be a much easier solution.
In the ideal world something like this would be amazing:
const ref = React.getParentRef();
Apologies if there is an obvious answer in the documentation, I'm very new to React and am unsure of the correct question to be asking or what to be looking for in order to find it in the docs.
You can return the ref from the hook
const useExample = () => {
const myRef = React.useRef(null)
const [stateValue, setStateValue] = useState("example");
useEffect(() => {
// Run some code...
}, []);
return [myRef , stateValue];
};
const Component = () => {
const [myRef , data] = useExample();
return (
<div ref={myRef}> /* <--- How do I gain access to this element */
<span>{ data }</span>
</div>
);
};
If data can be a component:
const useExample = () => {
const myRef = React.useRef(null);
const [stateValue, setStateValue] = React.useState("example");
React.useEffect(() => {
const parent = myRef?.current?.parentNode;
console.log(parent);
}, []);
return <div ref={myRef}>{stateValue}</div>;
};
const Component = () => {
const data = useExample();
return (
<div>
<span>{data}</span>
</div>
);
};
export default function App() {
return <Component />;
}
But then you have to access the parent node from the ref, I believe this may cause problems as a component is being returned, and its anti pattern
I want to extract some variables and the props like this
const MyComponent = (props, {data}) => {
return (
<div data-set={props["data-set"]}>
{data.name}
</div>
)
};
of course this doesn't work. How do I desctructure the data-set value or how do I keep props while destructuring data ??
Because it's only one variable I want to destructure, I could just use
const MyComponent = props => {
const { data } = props // adding this line here
return (
<div data-set={props["data-set"]}>
{data.name}
</div>
)
};
but if it were a bigger or more nested object, it my not be as cool
You are looking for rest:
const MyComponent = (props, { data, ...rest }) => {
return <div data-set={rest["data-set"]}>{data.name}</div>;
};
Im trying to create a HOC to get the ref of any component.
When I pass normal jsx, works well. But it doesnt work when I pass a functional component:
class GetRef extends React.Component {
state = {};
constructor(props) {
super(props);
this.renderChildren = this.renderChildren.bind(this);
}
componentDidMount() {
this.props.setRef(this.ref);
}
renderChildren() {
const childElement = React.Children.only(this.props.children);
return React.cloneElement(childElement, { ref: (el) => (this.ref = el) });
}
render() {
return <>{this.renderChildren()}</>;
}
}
const RefRegister = ({ children }) => {
const [ref, setRef] = useState();
console.log({ ref });
return <GetRef setRef={setRef}>{React.Children.only(children)}</GetRef>;
};
const Example = () => <div>example</div>;
export default function App() {
return (
<div className="App">
<RefRegister>
{/* <div>example</div> */} Works well in this case
<Example /> // Throws an error passing a component :(
</RefRegister>
</div>
);
}
Demo
use React.forwardRef:
const Example = React.forwardRef((props, ref) => <div ref={ref}>example</div>);
Demo - https://codesandbox.io/s/practical-proskuriakova-ywdj6