React.cloneElement clone already cloned element to add new props

Viewed 813

I have have TestWrapper component that clones element and is supposed to make the background blue. Test is also doing the same, but instead sets color. I was expecting the rendered element to have blue background and red text, but it is only red. Here is the example:

const Test: React.FC<{ element: React.ReactElement }> = ({ element }) => {
  return React.cloneElement(element, {
    const isSelected = useIsSelected();
    style: { ...element.props.style, color: isSelected ? 'red' : 'black' }
  });
};

const TestWrapper: React.FC<{ element: React.ReactElement }> = ({
  element
}) => {
  // BackgroundColor is blue
  const { backgroundColor } = useTheme();
  return React.cloneElement(element, {
    style: { ...element.props.style, background: backgroundColor }
  });
};

export function App() {
  return <TestWrapper element={<Test element={<h1>Heading</h1>} />} />;
}

How can I achieve this? I could do this differently, but I have to be able to access hook methods from Test and TestWrapper.

Simple codesandbox example: https://codesandbox.io/s/serene-bassi-ve1ym?file=/src/App.tsx

1 Answers

In TestWrapper you are cloning the Test component and applying your style props to it, which are not being passed down to the element that it's cloning itself. Just returning a cloned element doesn't create a referential equality where doing something to the component will affect the element it is cloning. You would need to give a style prop to Test and pass it down to the element being cloned:

const Test: React.FC<{
  style?: React.CSSProperties;
  element: React.ReactElement;
}> = ({ element, style }) => {
  return React.cloneElement(element, {
    style: { ...element.props.style, ...style, color: "red" }
  });
};

I made a fork here. Hopefully this helps!

Related