Can not render component correctly in ReactJS - reactjs

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()

Related

React, how to map comments to display them each in a banner

I'm new to React and I'm facing a problem that I can't solve. I want to map comments retrieved from the database at the front level. The concern being that in some cases I have several user comments that I want to display each in a banner. I almost got there but currently in the case that I am exploring, I have three comments but these three comments are displayed in succession in three identical banners. While I would like them to appear each in their respective banner. Thank you in advance for your help !
`const { actions } = useGetActionsFromReport(sarId, WorkflowActionEnum.ASK_FOR_MODIFICATION)
const comments = actions?.map((action) => action.comment || '')
return (
<ContentWithAccessControl error={suspiciousActivityReportError}>
<StepLayout>
<Styled.Container data-testid="sar-initialisation-step-container">
{comments && comments.map((comment) => {
return (
<Banner
key={comment}
label={t('redaction.initialisation.comments.label', { comments })}
testId="get-comments"
type={Type.INFORMATION} >
{t('redaction.initialisation.comments.label', { comments })}
</Banner>)})}`
enter image description here
You can use the index of the element in the array to display the comment in its respective banner:
const { actions } = useGetActionsFromReport(sarId, WorkflowActionEnum.ASK_FOR_MODIFICATION);
const comments = actions?.map((action, index) => action.comment || '');
return (
<ContentWithAccessControl error={suspiciousActivityReportError}>
<StepLayout>
<Styled.Container data-testid="sar-initialisation-step-container">
{comments && comments.map((comment, index) => {
return (
<Banner
key={`comment-${index}`}
label={t('redaction.initialisation.comments.label', { comment })}
testId="get-comments"
type={Type.INFORMATION}
>
{t('redaction.initialisation.comments.label', { comment })}
</Banner>
)
})}
</Styled.Container>
</StepLayout>
</ContentWithAccessControl>
);

React Function calling Function Method for HTML

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
}
})()}

Delay JSX evaluation

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>

When are components defined in functions evaluated? (React Hooks)

Suppose I have a component that renders a list item:
const ListItem = ({ itemName }) => {
return (
<div>
{itemName}
</div>
)
}
And because this list item is used in many places in my app, I define a custom hook to render the list and control the behavior of each list instance:
const useListItems = () => {
const [ showList, setShowList ] = useState(true)
const { listItemArray, isLoaded } = useListContext() // Context makes api call
const toggleShowList = setShowList(!showList)
function renderListItems() {
return isLoaded && !!listItemArray ? listItemArray.map((itemName, index) => (
<ListItem key={index} isVisible={showList} itemName={itemName}/>
))
:
null
}
// Some other components and logic...
return {
// ...Other components and logic,
renderListItems,
toggleShowList,
}
}
My first question is, when will the array of ListItems actually be evaluated ? Will the jsx resulting from renderListItems() be calculated every time renderListItems() is called? Or would it happen every time useListItems() is called?
Second question: if I call useListItems() in another component but don't call renderListItems(), does that impact whether the components are evaluated?
I have been struggling to find an answer to this, so thanks in advance.

How to place return code in a function: React

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}
/>);
}

Resources