Not able to get the previous props value in react - reactjs

I have a Detail list, based on the condition I need to show columns. If I am doing below approach then Its working fine, but on render method
return (
<DetailsList
isLoading={isLoading(this.props.loadProgress)}
items={this.props.solutionSubcomponents}
columns={
this.props.solutionSubcomponents.length > 0 &&
this.props.solutionSubcomponents[0].subcomponentType.logicalName ===
SolutionComponentTypes.EntityRelationship.logicalName
? this.buildColumns()
: this.buildRelationshipColumns()
}
renderOnEmpty={() => {
let props = this.getPropsOnEmpty();
return <EmptyState {...props} />;
}}
filter={this.props.searchFilter}
renderOnEmptySearch={() => {
let props = this.getPropsOnEmptySearch();
return <EmptyState {...props} />;
}}
This code is working fine but as this is not a good approach so I wnat to handle this on ComponentDidMount but not able to get the previous props value.
public componentDidUpdate(prevProps: ISolutionSubcomponentListProps): void {
if (
this.props.solutionSubcomponents.length > 0 &&
prevProps.solutionSubcomponents.length > 0 &&
this.props.solutionSubcomponents[0].subcomponentType.logicalName !==
prevProps.solutionSubcomponents[0].subcomponentType.logicalName
) {
if (
this.props.solutionSubcomponents.length > 0 &&
this.props.solutionSubcomponents[0].subcomponentType.logicalName ===
SolutionComponentTypes.EntityRelationship.logicalName
) {
this.columns = this.buildRelationshipColumns();
}
}
}
Please suggest how I can set the dynamic column to react life-cycle method instead of render method of detail list.

Related

React Hooks "useState/useEffect/useCallback" are called conditionally

Please tell me where do I need to put the list.length condition to remove the React Hooks are called conditionally error? I tried to wrap it in useEffect, but in this case an empty list is returned at the first render. It is important that the list is returned at the first render in the same way as with the logic in the code below.
const List = ({ list }) => {
if (list.length === 0) {
return <div>LOADING...</div>;
}
const [localList, setLocalList] = useState(list);
useEffect(() => {
setList(localList);
}, [localList]);
const handleChange = useCallback((id) => {
setLocalList((prevLocalList) =>
prevLocalList.map((item, index) => {
return index !== id ? item : { ...item, checked: !item.checked };
})
);
}, []);
return (
<>
{localList?.map((item, index) => (
<MemoRow key={index} {...item} handleChange={handleChange} />
))}
</>
);
};
The rendered result is returned at the end of the component, not at the beginning. Make that first operation part of the overall return at the end:
return (
list.length === 0 ?
<div>LOADING...</div> :
<>
{localList?.map((item, index) => (
<MemoRow key={index} {...item} handleChange={handleChange} />
))}
</>
);
Additionally, there is a logical issue in your component. When a parent component passes the list value, you are duplicating that in local state in this component. If the parent component changes the value of list, this component will re-render but will not update its local state.
Given the term "LOADING..." in the UI, this implies that's exactly what's happening here. So on a re-render, list.length === 0 is now false, but localList is still empty.
As a "quick fix" you can just update localList any time list changes:
useEffect(() => {
setLocalList(list);
}, [list, setLocalList]);
Of course, this will also over-write any local changes to localList if the parent component ever changes list again. But since this is duplicated state then it's not really clear what should happen in that case anyway. Perhaps you could only conditionally update it if localList is empty:
useEffect(() => {
if (localList.length === 0) {
setLocalList(list);
}
}, [list, setLocalList, localList]);
It's really up to you how you want to handle edge cases like that. But ultimately you're going to need to update localList after list has changed if you want those changes to be reflected in your local state.

Can not render component correctly in 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()

React hooks show more/less text like in youtube comments section

I have some array of comments like in youtube comments section and i wan't to make it show less/more button for long comment. I've stuck a little on a way to do it locally on item without total rerender (rebuild of block) or any states values.
function commentsGenerate() {
let block = comments.map((comment, i) => {
let splitLength = 400
let shortDesc = comment || '' // for null vals
let shortened = false
if (shortDesc.length > splitLength) shortened = true
shortDesc = shortDesc.substring(0, splitLength)
return (
<div key={i}>
<div>{`${shortDesc}${shortened ? '...' : ''}`}</div>
{shortened && <Button onCLick={ () => {'how?'} >More</Button>}
</div >
)
})
setSectionBlock(block)
}
You can't/shouldn't do this sort of thing without using state somewhere. In this case I suggest that you separate your comment code and state into a separate component which can handle its own expanded state. You can then use the state to adjust the rendering and styling of your output:
import React, { useState } from 'react'
// ... //
const Comment = ({comment:shortDesc = ''})=>{
let splitLength = 400
let shortened = false
const [isExpanded, setIsExpanded] = useState(false)
if (shortDesc.length > splitLength) shortened = true
shortDesc = shortDesc.substring(0, splitLength)
const handleToggle = ()=>{
setIsExpanded(!isExpanded)
}
return (
<div key={i}>
{!isExpanded ? <div>{`${shortDesc}${shortened ? '...' : ''}`}</div> : null}
{shortened && <Button onClick={handleToggle}>{isExpanded?'Less':'More'}</Button>}
</div >
)
}
Use this component in your mapping like this:
let block = comments.map((comment, i) => <Comment comment={comment} key={i} /> )

How to add functions to a plugin in react native

I am using a plugin named react-native-masonry-list to show the list of data in masonry view which works fine.But when i try to update a value in the data.The value gets updated but it is not reflected in the masonry list.I have checked the props in which the data gets updated.
When i saw the code in the plugin it has mentioned that only during the image / orientation / size gets changed the data is refreshed and state gets updated.
I would like to add a function like once the data gets changed it should refresh the list.But i don't know how to inherit the plugin without making any changes in their code.
Plugin Link: react-native-masonry-list
<MasonryList
sorted={true}
imageContainerStyle={styles.masonryImgView}
onPressImage={this.onSelectBranchItem}
renderIndividualHeader={res => {
return (
<DealsMasonryHeader
data={res}
onSelectedDeal={this.onSelectedDeal}
onSelectedFavourite={this.onSelectedFavourite.bind(this, res)}
favDisable={res.isfavourite}
onMapOpen={this.onMapOpen.bind(this, res)}
onSocialShare={this.socialShare.bind(this, res)}
/>
);
}}
renderIndividualFooter={res => {
return (
<DealsMasonryFooter
data={res}
onItemPress={this.onSelectBranchItem.bind(this, res)}
/>
);
}}
images={filterData}
/>
FilterData:
const { dealsList, currentUser } = this.props;
const { searchText, selectedBranchItem } = this.state;
let re = new RegExp(searchText, 'i');
const filterData = dealsList
? dealsList.filter((deal) => {
return !!(
deal.title.match(re) ||
deal.discount_percentage.toString().match(re) ||
deal.tags.toString().match(re)
);
})
: [];
I want to know whether it is possible to inherit a component in react native
In the react-native-masonry-list plugin -> MasonryList.js
componentWillReceiveProps = (nextProps) => {
if (nextProps.layoutDimensions.width && nextProps.layoutDimensions.height &&
nextProps.layoutDimensions.columnWidth && nextProps.layoutDimensions.gutterSize &&
nextProps.layoutDimensions.width !== this.props.layoutDimensions.width &&
nextProps.layoutDimensions.height !== this.props.layoutDimensions.height &&
!this.props.containerWidth) {
this.resolveImages(
nextProps.itemSource,
nextProps.images,
nextProps.layoutDimensions,
nextProps.columns,
nextProps.sorted
);
}
else if (nextProps.orientation !== this.props.orientation ||
nextProps.columns !== this.props.columns ||
nextProps.spacing !== this.props.spacing ||
nextProps.sorted !== this.props.sorted ||
nextProps.containerWidth !== this.props.containerWidth) {
this.resolveImages(
nextProps.itemSource,
this._calculatedData,
nextProps.layoutDimensions,
nextProps.columns,
nextProps.sorted
);
}
else if (nextProps.images !== this.props.images) {
this.resolveImages(
nextProps.itemSource,
nextProps.images,
nextProps.layoutDimensions,
nextProps.columns,
nextProps.sorted
);
}
}

Clear multiple select fields values in react-select from external action

I am using https://github.com/JedWatson/react-select to display set of single/multiple select lists in the UI. And now I need to clear all the fields values at once.
In the docs there are options to add a clear button into or next to the element - Clearable property. But I want to call it from outside the elements on the container component level, using redux states for example.
Component structure is as follows:
renderSelectFilters() {
const { filters } = this.props;
return (
filters.map((filter) => {
if (filter.type == 'Checkbox' || filter.type == 'Select') {
return (
<SelectContainer
key={filter.name}
name={filter.name}
options={filter.options}
multi={filter.type == 'Checkbox' ? true : false}
handleChange={this.handleChange}
clearValues={this.clearValues}
searchable={filter.type == 'Checkbox' ? true : false}
/>
);
}
})
)
}
clearFilters() {
//stuff here
}
renderClearFiltersButton = () => {
return (
<Button
text={'Clear Filters'}
onClick={this.clearFilters}
/>
)
}
render() {
return (
<div className="filters-bar">
<div className="some-class">
{this.renderSelectFilters()}
{this.renderClearFiltersButton()}
</div>
</div>
)
}
I've checked this solution React-select clear value while keeping filter but it's about setting the existing value not completely removing the value.
I would sync react-select value with redux so that when you clear the value in redux it would be automatically cleared on react-select.

Resources