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>
)};
Related
I have react component like given below:-
Now I have few input fields in child component. I want to update grandparent state when user focus child component input control.
GrandParent Code:-
const GrandParent=()=>
{
const [focusDiv, setFocusDiv] = useState(false);
return (
<>
<h1>GrandParent</h1>
<p>{focusDiv}</p>
<Parent setFocusDiv={setFocusDiv} />
</>
)
}
Parent Code:-
const Parent=()=>
{
return (
<>
<h1>Parent</h1>
<Child setFocusDiv={props.setFocusDiv} />
</>
)
}
Child Code:-
const Child=()=>
{
function focus_fun(e) {
if(e.target.id == "textID"){
props.setFocusDiv(true);
}
}
return (
<>
<h1>Child</h1>
<input type="text" id="textID" onFocus={focus_fun} />
</>
)
}
Thanks for your efforts!
You should also receive props in your Child and Parent
const Child=(props)=>
{
function focus_fun(e) {
if(e.target.id == "textID"){
props.setFocusDiv(true);
}
}
return (
<>
<h1>Child</h1>
<input type="text" id="textID" onFocus={focus_fun} />
</>
)
}
Same for Parent
Mehod #1
(not extensible very much and need to pass props around)
You need to define props input for your components pass the props from grandparent to child and also convert the boolean to a string otherwise it won't render the value:
const GrandParent = () => {
const [focusDiv, setFocusDiv] = React.useState(false);
return (
<React.Fragment>
<h1>GrandParent</h1>
<p>{focusDiv.toString()}</p>
<Parent setFocusDiv={setFocusDiv} />
</React.Fragment>
);
};
const Parent = (props) => {
return (
<React.Fragment>
<h1>Parent</h1>
<Child setFocusDiv={props.setFocusDiv} />
</React.Fragment>
);
};
const Child = (props) => {
function focus_fun(e) {
if (e.target.id == "textID") {
props.setFocusDiv(true);
}
}
return (
<React.Fragment>
<h1>Child</h1>
<input type="text" id="textID" onFocus={focus_fun} />
</React.Fragment>
);
};
ReactDOM.render(<GrandParent />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>
Mehod #2
Use CreateContext and useContext hooks to pass the state and setter methods directly to any child in the tree without using props:
const FocusContext = React.createContext();
const GrandParent = () => {
const [focusDiv, setFocusDiv] = React.useState(false);
return (
<React.Fragment>
<h1>GrandParent</h1>
<p>{focusDiv.toString()}</p>
<FocusContext.Provider value={{ focusDiv, setFocusDiv }}>
<Parent />
</FocusContext.Provider>
</React.Fragment>
);
};
const Parent = () => {
return (
<React.Fragment>
<h1>Parent</h1>
<Child />
</React.Fragment>
);
};
const Child = () => {
const focus = React.useContext(FocusContext);
function focus_fun(e) {
if (e.target.id == "textID") {
focus.setFocusDiv(true);
}
}
return (
<React.Fragment>
<h1>Child</h1>
<input type="text" id="textID" onFocus={focus_fun} />
</React.Fragment>
);
};
ReactDOM.render(<GrandParent />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>
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}
</>
);
}
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 have an array of objects:
const items = [
{
head: 'Contributions',
subhead: 'Search by person.',
body: 'Empty'
},
...
];
going into this accordion:
const SearchMenu = (props) => {
const [selectedCard, setSelectedCard] = useState(null);
return (
<Accordion>
{items.map((item, index) => (
<div className="searchOption" key={index.toString()}>
<Card className="card">
{renderHeader(index, selectedCard, setSelectedCard, item)}
<Accordion.Collapse eventKey={index.toString()}>
<div className="body">
<Card.Body className="card card-body">{item.body}</Card.Body>
</div>
</Accordion.Collapse>
</Card>
<hr className="divider"></hr>
</div>
))}
</Accordion>
);
};
and the body of each object will be a functional component. How can I pass this component into the body of the objects? I have not made the components yet.
You can add like this :
const items = [
{
head: 'Contributions',
subhead: 'Search by person.',
body: <Test />
}
];
const Test = () => <h1>test</h1>
const items = [
{
head: 'Contributions',
subhead: 'Search by person.',
body: <Test />
}
];
const SearchMenu = (props) => {
return (
<div>
{items.map((item, index) => (
<div>
{item.body}
</div>
))}
</div>
);
};
ReactDOM.render(<SearchMenu />, document.querySelector('#root'));
<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="root"></div>
This is react-window plugin: https://github.com/bvaughn/react-window
I am using this to render simple list of "Rows".
This is Row comp in which I am try to pass function and const idTestProps=''
class Row extends PureComponent {
render() {
const { index, style } = this.props;
let label;
if (itemStatusMap[index] === LOADED) {
label = `Row ${index}`;
} else {
label = "Loading...";
}
return (
<div className="ListItem" style={style}>
{label}
</div>
);
}
}
This is the Container comp which should pass function and one props to the Row comp:
const outerElementType = forwardRef((props, ref) => (
<div ref={ref} onClick={handleClick} {...props} />
));
export default function App() {
return (
<Fragment>
<InfiniteLoader
isItemLoaded={isItemLoaded}
itemCount={1000}
loadMoreItems={loadMoreItems}
>
{({ onItemsRendered, ref }) => (
<List
className="List"
height={150}
itemCount={1000}
itemSize={35}
// This is outerElementType is way to pass some function down to Row
outerElementType={outerElementType}
width={300}
>
{Row}
</List>
)}
</Fragment>
);
I successfully pass 'function' and works but property not.
How to pass props down in same time with function?
This is codesandbox example:
https://codesandbox.io/s/4zqx79nww0
I have never used react-window but maybe you can do something like this:
import React, { forwardRef } from "react";
import ReactDOM from "react-dom";
import { FixedSizeList as List } from "react-window";
import "./styles.css";
const Row = props => ({ index, style }) => (
<div className={index % 2 ? "ListItemOdd" : "ListItemEven"} style={style}>
Row {index} {props.test}
</div>
);
function handleOnWheel({ deltaY }) {
// Your handler goes here ...
console.log("handleOnWheel()", deltaY);
}
const outerElementType = forwardRef((props, ref) => (
<div ref={ref} onWheel={handleOnWheel} {...props} />
));
const Example = () => (
<List
className="List"
height={150}
itemCount={1000}
itemSize={35}
outerElementType={outerElementType}
width={300}
>
{Row({ test: "test" })}
</List>
);
ReactDOM.render(<Example />, document.getElementById("root"));