I have a component where I need to pass a HTML element as a prop to another element
const MyText = () => {
return (
<>
<h1>Sample heading</h1>
</>
)
}
return (
<div>
<MyComponent Text={MyText} onClose={() => setShow(false)} show={show} />
</div>
);
MyComponent.js
export default function MyComponent(props) {
return (
<>
{props.Text}
</>
);
}
Issue: I'm not getting anything rendered on the screen. Am I missing something here?
There are two ways.
Option 1: Passing a component type (or class if you are coming from OOP background)
const MyText = () => {
return (
<>
<h1>Sample heading</h1>
</>
)
}
return (
<div>
<MyComponent Text={MyText} onClose={() => setShow(false)} show={show} />
</div>
);
const MyComponent = ({ Text }) => {
return (
<>
<Text />
</>
);
}
Option 2: Passing a component (or instance if you are coming from OOP background)
const MyText = () => {
return (
<>
<h1>Sample heading</h1>
</>
)
}
return (
<div>
<MyComponent text={<MyText />} onClose={() => setShow(false)} show={show} />
</div>
);
const MyComponent = ({ text }) => {
return (
<>
{text}
</>
);
}
Related
In my React app I am rendering some blocks:
const MyBlocks = ({ id }: { id: string }) => {
const { data, loading } = useQuery<GqlRes>(BlocksQuery, {
ssr: false,
errorPolicy: 'all',
variables: {
blocksId: parseInt(id, 10),
},
});
if (loading) {
return <CircularProgress />;
}
return (
<React.Fragment>
{data?.blocks.map((item, i) => (
<Block key={String(i)} data={item} />
))}
</React.Fragment>
);
};
export default MyBlocks;
When there are more than 3 blocks rendered by the backend, I want to add a placeholder <div> (filled by a third party script) after the third block. So I get:
<Block>
<Block>
<Block>
<div id="placeholder" />
<Block>
<Block>
How do I do that, what's a nice solution for this?
<React.Fragment>
{data?.blocks.map((item, i) => (
<>
<Block key={String(i)} data={item} />
{ i === 2 && <div id="placeholder" /> }
</>
))}
</React.Fragment>
I'm trying to pass a remove function into the map with a parameter, but it's like I'm not using my function. What would be the correct way to call ''remove(i)'' inside InputTime in onRemove?
Component
render() {
const {remove} = this.props;
return(
<>
<InputTime
onRemove={() => remove(i)}
{...this.props}
/>
</>
)
}
}
// another class
handlerRemove(){
...
}
return(
<Component remove={() => this.handlerRemove()} />
)
Here you go with a solution
render() {
const {remove} = this.props;
return(
<>
<InputTime
time={hour}
onRemove={() => remove(i)}
{...this.props}
/>
</>
)
}
}
As you have done the destructing using const {values, remove} = this.props;, then you don't need to add this for prop function.
Current code onRemove={() => this.remove(i)}
Change you code to onRemove={() => remove(i)}
As the user change the code a bit, based on that the solution
Component
render() {
const {remove} = this.props;
const arg = "test";
return(
<>
<InputTime
onRemove={() => remove(arg)}
{...this.props}
/>
</>
)
}
}
// another class
handlerRemove(passedArg){
console.log("passedArg", passedArg);
}
return(
<Component remove={this.handlerRemove} />
)
I have the following list of React components and can't change this format.
How could I render this list on my page by looping over it in some way?
const allComponents = isValid => [
{
Component: (
<ComponentA
isTransparent={true}
/>
),
},
{
Component: (
<div>
{<ComponentB/>}
</div>
),
},
!isValid && {
Component: (
<div>
{<ComponentC/>}
</div>
),
},
].filter(Boolean);
Within my return block tried the following:
return (
<Fragment>
{allComponents(false).map(c => (
{c}
))}
</Fragment>
);
End up with following error.
Error! Objects are not valid as a React child.
(found: object with keys {c}). If you meant to render a
collection of children, use an array instead.
But the above allComponents is an array.
Could I please get some advice on this pls.
The JSX stored in the the array returned by allComponents() needs to be returned from a valid function component. You can either turn the Component properties into functions
{
Component: () => (
<ComponentA />
),
},
// And then call it in the map()
{allComponents(false).map(c => (
c.Component()
))}
or return the JSX from an IIFE inside the map() call
{allComponents(false).map(c => (
(() => c.Component)()
))}
Working snippet
const App = () => {
const allComponents = isValid => [
{
Component: (
<ComponentA />
)
,
},
{
Component: (
<div>
{<ComponentB />}
</div>
)
,
},
!isValid && {
Component: (
<div>
{<ComponentC />}
</div>)
,
},
].filter(Boolean);
return (
<div>
<p>isValid: False</p>
<div>
{allComponents(false).map(c => (
(() => c.Component)()
))}
</div>
<p>isValid: True</p>
<div>
{allComponents(true).map(c => (
(() => c.Component)()
))}
</div>
</div>
);
}
const ComponentA = () => {
return (
<div>Component A</div>
)
}
const ComponentB = () => {
return (
<div>Component B</div>
)
}
const ComponentC = () => {
return (
<div>Component C</div>
)
}
ReactDOM.render(
<App />,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
return (
<Fragment>
{allComponents(false).map(c => (
{c.component}
))}
</Fragment>
);
you are attempting to render an object in your above example and not the component itself. IMO I would update your overall structure
I am a beginner at React. I wrote this code and got an Error
import { DoItem } from '../MyComponents/DoItem'
export const ToDo = (props) => {
return (
<div className="container">
<h3 className="text-center">To Do List</h3>
{props.todos.map((todo) => {
return (
<>
<DoItem const todo={todo} onDelete = {props.onDelete} />
<hr />
</>
)
})}
</div>
)
}
Here's the error Each child in a list should have a unique "key" prop.
I looked online and found that I have to use a key.
I inserted the key
<DoItem const todo={todo} key={todo.nos} onDelete = {props.onDelete} />
But the error still didn't go after I reload the page.
Please add a unique key to every item
{props.todos.map((todo, index) => {
return (
<div key={index}>
<DoItem const todo={todo} onDelete = {props.onDelete} />
<hr />
</div>
)
})}
Please have a try and let me know if it works or not.
import { DoItem } from '../MyComponents/DoItem'
export const ToDo = (props) => {
return (
<div className="container">
<h3 className="text-center">To Do List</h3>
{props.todos.map((todo, i) => {
return (
<key={i}>
<DoItem const todo={todo} onDelete = {props.onDelete} />
<hr />
</>
)
})}
</div>
)
}
Yeah, you should add a key prop to anytime you're rendering an Array.
import { DoItem } from '../MyComponents/DoItem'
export const ToDo = (props) => {
return (
<div className="container">
<h3 className="text-center">To Do List</h3>
{props.todos.map((todo,index) => {
return (
<div key={index | the id of the todo}>
<DoItem const todo={todo} onDelete = {props.onDelete} />
<hr />
</div>
)
})}
</div>
)
}
How can I add additional props to a JSX.Element variable that is passed as a prop?
First I create the variable like so
const leftIcon = <SmallIcon icon="note" color={colors.red} />
Then it is passed to my function component and used like
const ScreenHeader: React.FunctionComponent<ScreenHeaderProps> = ({
leftIcon = <></>,
}) => {
return (
<View>
<Header
leftComponent={leftIcon}
/>
</View>
)};
How can I add an additional styles prop to the "leftIcon" variable before it is used in Header?
If you initialize a variable with a React component the way you're doing it right now (const leftIcon = <SmallIcon />), then you won't be able to pass additional props into it.
Here's a possible solution:
// make `LeftIcon` into a function so that you
// can use it in the following way: `<LeftIcon />`
const LeftIcon = (props) => (
<div className="LeftIcon" onClick={() => {}} {...props}>
<p>I am a left icon!</p>
<p>Additional props: {JSON.stringify(props)}</p>
</div>
);
const ScreenHeader = ({ leftComponent = null }) => {
const CustomLeftComponent = leftComponent ? leftComponent : null;
const greenComponent = CustomLeftComponent
? <CustomLeftComponent style={{ color: "green" }} />
: null;
return (
<div>
<p>I am a screen header!</p>
{greenComponent}
</div>
);
};
function App() {
return (
<div className="App">
<ScreenHeader leftComponent={LeftIcon} />
<hr />
<ScreenHeader />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById("app")
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Alternatively, you could pass additional props to the LeftIcon component prior to using it inside ScreenHeader:
// make `LeftIcon` into a function so that you
// can use it in the following way: `<LeftIcon />`
const LeftIcon = (props) => (
<div className="LeftIcon" onClick={() => {}} {...props}>
<p>I am a left icon!</p>
<p>Additional props: {JSON.stringify(props)}</p>
</div>
);
const ScreenHeader = ({ leftComponent = null }) => {
return (
<div>
<p>I am a screen header!</p>
{leftComponent}
</div>
);
};
function App() {
return (
<div className="App">
<ScreenHeader leftComponent={<LeftIcon style={{ color: "green" }} />} />
<hr />
<ScreenHeader />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById("app")
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
You can use React.cloneElement to add additional props
const ScreenHeader: React.FunctionComponent<ScreenHeaderProps> = ({
leftIcon = <></>,
}) => {
return (
<View>
<Header
leftComponent={React.cloneElement(leftIcon, {className: classes.myClass})}
/>
</View>
)};