Is having a Functional Method being called in the Main Function bad practice? Below , my ProductDashboard is calling productbody html and other components (in different files) . Is having"return productBody" an incorrect practice? I want to isolate some of the extra html into a method within the component, instead of a separate file.
*If not, is there an alternative to isolate the html without using another file?
My code currently works regardless.
citing this article: https://dev.to/igor_bykov/react-calling-functional-components-as-functions-1d3l
const ProductDashboard = () => {
const productBody = () => {
return (
<Box>
<ProductHeader/>
<ProductList
productId={encounter.patientId}
serviceLocationId={encounter.serviceLocationGuid}
showTotalBalance
/>
......
</Box>
);
};
return (
<Box data-testid="financial-information">
{(() => {
if (loadingError) {
return <LoadingError />;
} else if (isLoading) {
return <CircularLoading />;
} else if (encounter == null || encounter?.length === 0) {
return <EncounterAlert />;
} else {
return productBody; // <<---- this line here
}
})()}
Related
I have a function which returns JSX to some React components. I realized that I need to also include some of the state variables of those components in the JSX. Each component might need to change the JSX or the values in it (or both). See the code below.
export function getDisplayBlock(a: number) {
if (a == 1) {
return <>{title}</>;
} else if (a == 2) {
return (
<>
{title}
{subtitle}
</>
);
} else if (a == 3) {
return (
<>
<img src={companyLogo} />
{caption}
</>
);
}
}
This doesn't work because in getDisplayBlock the title,subtitle,companyLogo and caption are not known.
Some workarounds I considered:
Pass all those (title,subtitle) as another argument to the function. this requires recalling the function everytime these variables are updated
Return a string instead of JSX and substitute the parameter values with regex, then use dangerouslySetInnerHTML.
"Hack it" (advantage here is that you call the function once to get the JSX that the component needs):
export function getDisplayBlock(this: any, a: number) {
if (a == 1) {
return () => <>{this.state.title}</>;
} else if (a == 2) {
return () => (
<>
{this.state.title}
{this.state.subtitle}
</>
);
} else {
return () => (
<>
<img src={this.state.companyLogo} />
{this.state.caption}
</>
);
}
}
Any ideas appreciated!
There is no way to refresh the return value of the function without recalling it. You can checkout if something like React.Context is what you're interested in though it won't really improve the amount of calls to the rendering code and thus performance (is that the concern ?).
I don't fully understand the use case, i.e. if the parent components will always ever only render one of the jsx return values, then spread them out into separate components instead of having them in the same function.
The end result will be the same, the function will need to be recalled every time the state updates. But the code will be much cleaner.
Update per refreshed question:
If you want to control the JSX passed in, maybe rendering children in the subcomponent will work for you like so:
const GetDisplayBlock = props => {
const { a } = props;
return (
<>
{a == 1 ? (
<>{props.children}</>
) : a == 2 ? (
<>
<diV>{props.children}</diV>
</>
) : (
<>
<p>{props.children}</p>
</>
)}
</>
);
};
...
Then in the rendering call, you may call it like so:
<GetDisplayBlock a={3}>
<div>buyaka</div>
</GetDisplayBlock>
I'm trying to create a helper function that nest a passed element/component as a children of itself of several depths.
So something like this
const Chain = nested(4, () => <div />)
return (
<Chain />
)
Should render divs nested 4 levels
<div>
<div>
<div>
<div />
</div>
</div>
</div>
I implemented the function in React like this.
/** nest the given element in itself of specified depth */
export const nested = (
depth: number,
element: () => JSX.Element
): FC<any> => () => {
let chain = element();
for (let i = 0; i < depth; i++) {
chain = React.createElement(
element().type,
element().props,
chain
);
}
return chain;
};
It renders and displays the nested Divs correctly, but gives back an error message i don't understand how to correct.
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.
Try going with a recursive solution.
const createInnerElements = (count, childElement) => {
if (count == 1) {
return <div>{childElement}</div>
}
return createInnerElements(count - 1, <div>{childElement}</div>);
}
// in your parent component,
return <>{createInnerElements(4, <></>)}</>
Here's the solution I settled with. Credit to #senthil balaji's answer.
export const NestedTags = (depth: number, childElement: JSX.Element): JSX.Element => {
if (depth === 1) {
return (
<childElement.type {...childElement.props}>
{childElement}
</childElement.type>
);
}
return NestedTags(
depth - 1,
<childElement.type {...childElement.props}>
{childElement}
</childElement.type>
);
};
and should be wrapped in a React Fragment using used in parent.
<>
{NestedTags(5, <div className='segment'><div>)}
</>
The caveat for this is that it can't nest when passed a React Component Type like so:
const Test = ({className}: {className: string}) => (
<a className={className}>hello</a>;
)
//...
return (
<>
{NestedTags(5, <Test className='test'/>}
</>
)};
i am using useHook named useGetCompanyByItemId in the return statement.
and so i am getting the error
"react hook cannot be called in a callback function"
what i am trying to do?
i am querying for owneditems and shareditems.
and i display both the items. in the Content div i do mapping and there i am calling the useGetCompanyByItemId hook and i get the error.
below is my code,
function Parent() {
const ownedItems = [{ //somearray of objects}];
const sharedItems = [{//somearray of objects}];
const getCurrentItems = () => {
return ownedItems.concat(sharedItems);
}
return (
<Wrapper>
{getCurrentItems.length> 0 &&
<FirstWrapper>
//somedivs
</FirstWrapper>
<Content>
{springProps.map((index) => {
const item = getCurrentItems()[index];
const isSharedItem = item && item.cognitoId !== cognitoId;
const company = useGetCompanyByItemId(item.id); //here is the error
return (
<>
{isSharedItem &&
<div>
<span>company</span>
</div>
}
</>
}
)
}
);
</Content>
</Wrapper>
);
}
i am not sure how to fix this. i need to pass the item.id for the useGetCompanyById hook and i dont know how to pass that item.id from outside the return statement since that would fix that error.
could someone help me fix this error. thanks.
Extract this logic to a component
function Item({ item, isSharedItem }) {
const company = useGetCompanyByItemId(item.id);
return (
<>
{isSharedItem && (
<div>
<span>company</span>
</div>
)}
</>
);
}
and then use it in your loop
springProps.map((index) => {
...
return <Item item={item} isSharedItem={isSharedItem} key={index} />
I can see two ways of refactoring here:
Option 1: If you dont have control over the custom hook to modify
Extract the iteration into a component:
const Company = ({itemId, isSharedItem}) => {
const company = useGetCompanyByItemId(itemId);
return (<>
{isSharedItem &&
(<div>
<span>{company}</span>
</div>)
}
</>);
}
Use the above component while you iterate.
Option 2: If you have control over the custom hook:
I would recommend to refactor custom hook to return a method than object. Sample usage:
const {getCompanyByItemId} = useFetchCompany();
.
.
.
anywhere in the code,
getCompanyByItemId(itemId)
Obvious advantage with above option:
Readable and extendable and use it anywhere and even pass around
You don't have to worry about refactoring and code splitting just not to break hook rules(do so if it makes sense ofcourse).
I have a component which renders post on page.
On this component I have comments section.
In addition I have 2 components one for if user is not comment author and another one if the user is comment author.
Difference is that if user is author of the comment,comments will render with delete/edit buttons.
Now about the problem.
const checkCommentAuthor = (): boolean => {
return comments.map((item: any): boolean => {
if (item.username === localStorage.getItem("username")) {
return true;
} else {
return false;
}
});
};
return (
checkCommentAuthor ? (
< IsCommentAuthor />
) : ( <IsNotCommentAuthor/> )
);
};
If I have 2 comments on the post and let's say only one comment belongs to me(I mean I'm the author) the function will return <IsCommentAuthor/> for both of them.
Is there any possible way to return <IsNotCommentAuthor/> for some comments and < IsCommentAuthor /> for some?
You can simply return components in functions also.
Like
const checkCommentAuthor = () => {
return comments.map((item) =>
item.username === localStorage.getItem('username') ? (
<IsCommentAuthor />
) : (
<IsNotCommentAuthor />
)
)
}
Also in your code, there are two return() defined so only first one will return the function and code will never reach to next return()
I currently have a react project I'm working on. My render method looks like this going into my return method:
render() {
let elements = [];
this.dropdownCounter().forEach(item => {
if(item != "attributeProduct") {
console.log('setting');
elements.push(
<Dropdown
title={this.state[item][0]['title']}
arrayId={item}
list={this.state[item]}
resetThenSet={this.resetThenSet}
/>
);
}
});
this.state.attributeProduct.map(attributeItem => {
elements.push(
<Dropdown
title={attributeItem.name}
arrayId='attributeMetaProduct'
list={
this.state.attributeMetaProduct.filter(metaItem => metaItem.attribute_id == attributeItem.ID)
}
resetThenSet={this.resetThenSet}
/>
);
});
return (
I have a lot of code going on in the render area due to different drop downs dependent on other methods. Is there a way that I can do something like this instead?
render() {
allMyPrereturnStuff()
return()
}
Then just place all this code in allMyPrereturnStuff()? I've tried creating this function and passing everything there but it doesn't work due to all the "this". Any ideas?
Yes, you can easily drop in normal javascript expressions into JSX:
return (
<div>
{this.renderStuff()}
{this.renderOtherStuff()}
{this.renderMoreStuff()}
</div>
);
You can even base it on flags:
const shouldRenderMoreStuff = this.shouldRenderMoreStuff();
return (
<div>
{this.renderStuff()}
{this.renderOtherStuff()}
{shouldRenderMoreStuff ? this.renderMoreStuff() : null}
</div>
);
Do note that it is often an anti-pattern to have render* methods in your components other than the normal render method. Instead, each render* method should probably be its own component.
Don't forget to bind your allMyPrereturnStuff() method in the constructor so "this" will work inside it.
constructor(props) {
super(props);
// ... your existing code
this.allMyPrereturnStuff = this.allMyPrereturnStuff.bind(this);
}
allMyPrereturnStuff = (params) => {
// ... all the code
}
However, you might want to consider breaking out the code to components, which is more Reacty way to do things.
For example, you could refactor this
this.state.attributeProduct.map(attributeItem => {
elements.push(<Dropdown
title={attributeItem.name}
arrayId='attributeMetaProduct'
list={
this.state.attributeMetaProduct.filter(metaItem => metaItem.attribute_id == attributeItem.ID)
}
resetThenSet={this.resetThenSet}
/>);
});
To something like (somewhat pseudocody):
const DropdownList = (props) => {
return (<Dropdown
title={props.attributeItem.name}
arrayId='attributeMetaProduct'
list={props.list}
resetThenSet={props.resetThenSet}
/>);
}
And in the original component's render function, have something like
render() {
return (this.state.attributeProduct.map(attributeItem => {
<DropdownList attributeItem={attributeItem}
list={ this.state.attributeMetaProduct.filter(metaItem => metaItem.attribute_id == attributeItem.ID) }
resetThenSet={this.resetThenSet}
/>);
}