React native conditional rendering an array with 2 conditions - javascript

I have an array with several objects. Inside each object i have a field called type that can be either a or b. I am trying to conditional render a specific view for type a and a specific view for type b.
Here is my array:
DATA=[
{id:1, type:'a'},{id:2,type:'b'}
]
return(
{DATA.type === 'a' ?
<Text>I am type A</Text>
:
<Text>I am Type B </Text>
}
)
When i do that nothing appears on the screen.
UPDATE
DATA=[
{id:1, type:'a',locked:true},
{id:2,type:'b',locked:false}
]
const[isLocked,setIsLocked]=useState(false)
return(
{isLocked && DATA.map((item) => item.type === 'a')) ?
<Text>I am type A</Text>
:
null}
)
it is still not working. I am type A still appears on all pages of my carousel.

You need to map your array to accomplish it. Otherwise, you can't get the correct key.
DATA = [
{ id:1, type:a },
{ id:2, type:b },
]
return(
DATA.map((item) => item.type === 'a'
? <Text>I am type A</Text>
: <Text>I am Type B </Text>
))

Related

Javascript splice array method is not working as expected

,
I was working out with splice methods of js , but as it may seem it was not working exactly as it should remove any element from an array .
Currently its only deleting the last element from array even after providing the index value for it to delete from the array. in console.log i get the perfect output after deleting anything , but in UI part it does not update as it should , its only removing the last element from array even if i click on delete other item . How can i resolve this ?
Here's what i've tried so far :
const add_actions_options = [
{value : "Postback" , label:intl.formatMessage({ id: 'POSTBACK' })},
{value : "Uri" , label:intl.formatMessage({ id: 'URI' })}
]
const [ actions , setActions ] = useState<any | undefined>([{type : add_actions_options[0].value , label : "" , data : ""}])
const [selectOptions, setSelectOptions] = useState<any>(add_actions_options);
function addAction(){
if(actions.length < 4 ){
setSelectOptions([...add_actions_options])
setActions([...actions , {type : selectOptions[0].value , label : "" , data : ""}])
} else {
toast(intl.formatMessage({ id: 'MAX.ALLOWED.4' }), { type: "error" })
}
}
function deleteAction(index){
if(actions.length === 1 ){
toast(intl.formatMessage({ id: 'MIN.ALLOWED.1' }), { type: "error" })
} else {
const updatedFields = [...actions];
updatedFields.splice(index, 1);
console.log('index : ' , index)
console.log('updatedFields : ' , updatedFields)
setActions(updatedFields);
}
}
<div className='row my-6'>
<div className='col-lg-3 py-2'>
<h4><label className="form-label">{intl.formatMessage({ id: 'ACTIONS' })}*</label></h4>
<button className='btn btn-primary btn-sm btn-block' onClick={() => addAction()}>
<KTSVG path='/media/icons/duotune/arrows/arr075.svg' className='svg-icon-2' />
{intl.formatMessage({id: 'ADD.ACTION'})}
</button>
</div>
</div>
<div className='row my-6 '>
{ actions.map((item , index) => {
return(
<div key={index} className='row my-6'>
<div className='col-lg-4 py-2'>
<h4><label className="form-label">{intl.formatMessage({ id: 'TEMPLATE.TYPE' })}*</label></h4>
<Select
onChange={(value) => handleTypeChange(index, value)}
options={selectOptions}
/>
</div>
<div className='col-lg-3 py-2'>
<h4><label className="form-label">{intl.formatMessage({ id: 'TEMPLATE.LABEL' })}*</label></h4>
<input
{...formik_buttons_type.getFieldProps('action.label')}
className="form-control form-control-lg form-control-solid"
name='action.label'
id='action_label'
type="text"
maxLength={30}
onChange={(event) => handleLabelChange(index, event.target.value)}
value={actions.label}
required
onInvalid={(e) => checkLabelValidation(e)}
onInput={(e) => checkLabelValidation(e)}
/>
</div>
<div className='col-lg-3 py-2'>
<h4><label className="form-label">{intl.formatMessage({ id: 'TEMPLATE.DATA' })}*</label></h4>
<input
{...formik_buttons_type.getFieldProps('action.data')}
className="form-control form-control-lg form-control-solid"
name='action.data'
id='action_data'
type="text"
maxLength={100}
onChange={(event) => { handleDataChange(index, event.target.value); }}
value={actions.data}
required
onInvalid={(e) => checkDataValidation(e)}
onInput={(e) => checkDataValidation(e)}
/>
</div>
<div className='col-lg-2 py-2 mt-10'>
<OverlayTrigger
delay={{ hide: 50, show: 50 }}
overlay={(props) => (
<Tooltip {...props}>
{intl.formatMessage({ id: 'DEL.ACTION' })}
</Tooltip>
)}
placement="top">
<button
type='button'
style={{display: index === 0 ? 'none': 'inline-block'}}
className='btn btn-icon btn-md btn-bg-light btn-color-danger me-1'
onClick={() => deleteAction(index)}
>
<i className='fa fa-trash'></i>
</button>
</OverlayTrigger>
</div>
</div>
)
})}
</div>
I am able to receive exact index number perfect output from the logs below in deleteAction fields , but the view in browser deletes the last column(index) from the array of actions. :
console.log('index : ' , index)
console.log('updatedFields : ' , updatedFields)
can anyone help me with this ?
code sand box : https://codesandbox.io/s/vibrant-christian-bktnot
Thanks and Regards !
Whenever using the index as a key for an element. We have to ensure we are not modifying the state array to avoid bugs. If you are modifying as #Dave suggested use unique keys.
The problem here is using the index as key, When we remove an element from an array react compares the previous keys [0,1,2,3] with new keys [0,1,2].
If you notice closely, Even if we remove index (1) using splice(1,1) method. The elements which are rendered again have starting index of 0.
React compares keys previous keys [0,1,2,3] with new keys [0,1,2] and finds out that index 3 is removed hence it every time removes the 3rd element in the above example (or the last index) from DOM. However, your state is reflecting the correct array element.
To avoid this use a unique key.
Codesandbox for a working example.
If you are not having keys in objects, To generate unique keys we can use one of the following as per your use case:
crypto.randomUUID();
Date.now().toString(36) + Math.random().toString(36).substr(2)
https://reactjs.org/docs/lists-and-keys.html
We don’t recommend using indexes for keys if the order of items may change. This can negatively impact performance and may cause issues with component state.
When you do
{ actions.map((item , index) => {
return(
<div key={index}
you are pretty much asking for issues with component state. When react is trying to determine which element in the UI to update, and it sees "the array with a length of 6 now has a length of 5, which element do I need to remove", it will find the one with a key that no longer exists, in this case the one at the end since you used index as key, and will remove it.
I would probably do
function randomId(){
const uint32 = window.crypto.getRandomValues(new Uint32Array(1))[0];
return uint32.toString(16);
}
const [ actions , setActions ] = useState<any | undefined>([{id: randomId(), type : add_actions_options[0].value , label : "" , data : ""}])
function addAction(){
if(actions.length < 4 ){
setSelectOptions([...add_actions_options])
setActions([...actions , {id: randomId(), type : selectOptions[0].value , label : "" , data : ""}])
} else {
toast(intl.formatMessage({ id: 'MAX.ALLOWED.4' }), { type: "error" })
}
}
{ actions.map((item) => {
return(
<div key={item.id}
where randomId can be anything that gives you a unique ID, or (even better) if there is an existing property on the actual data that uniquely identifies it, you could use that.

javascript filter returning empty array even if the values exist in array object being searched

I am having difficulty using the javascript filter in a react native project. I need help filtering an array of objects in a typescript react native project
The main issue I am having is in the function called onSearchTextChanged specifically the search method which uses a javascript filter
Here's the main code for the screen below
const renderItem = ({ item }) => (
<Pressable style={cardUtilities} android_ripple={{ color: "#dddddd" }} onPress={() => onTapItemHandler(item)}>
<View style={main}>
<Text style={utilityname}>{item.title}</Text>
<Text style={utilityDesc}>{item.description}</Text>
</View>
</Pressable>
)
const onSearchTextChanged = (e) => {
setSearchQuery(e)
search()
}
const search = () => {
console.log("inside handleSearchButtonPressed")
if (!itemListStore) {
return
}
// const text = searchQuery.toLowerCase()
console.log("inside 100")
console.log(itemListStore)
// array of output objects
const filteredObjs = itemListStore.filter(
// eslint-disable-next-line array-callback-return
({ item }) => {
(JSON.stringify(item?.description[0]))?.toLowerCase().includes(searchQuery.toLowerCase()) ||
(JSON.stringify(item?.title[0]))?.toLowerCase().includes(searchQuery.toLowerCase()) ||
( JSON.stringify(item?.link[0]))?.toLowerCase().includes(searchQuery.toLowerCase())
})
console.log("filteredObjs", JSON.stringify(filteredObjs))
console.log(typeof filteredObjs)
console.log(filteredObjs.length)
console.log("inside 400")
console.log("searchQuery ", searchQuery)
if (!searchQuery || searchQuery === "") {
console.log("1")
} else if (!Array.isArray(filteredObjs) && !filteredObjs.length) {
console.log("2")
// set no data flag to true so as to render flatlist conditionally
setNoData(true)
} else if (Array.isArray(filteredObjs)) {
console.log("3")
setNoData(false)
setItemListStore(filteredObjs)
}
}
return (
<SafeAreaView style={container}>
{/* <Loader loading={loading} /> */}
<View style={fullContainer}>
<View style={MAIN_IMG}>
<Image style={$welcomeLogo} source={Images.appLogo} resizeMode="contain" />
</View>
<View style={TOP_143}>
<TextInput
style={textInputStyle}
onChangeText={onSearchTextChanged}
underlineColorAndroid="transparent"
placeholder="Search Here"
value={searchQuery}
/>
</View>
<FlatList
data={itemListStore}
renderItem={renderItem}
keyExtractor={() => {
return uuid.v4() + ""
}} />
</View>
</SafeAreaView>
)
Here is sample data made up of an array of objects to be filtered
[
{
"description":[
"At company; Professionals, we believe that individuals and organisations need to apply a whole new level of thinking to navigate and thrive in the emerging world. We no longer have the luxury of conducting business as usual. The organisation or individual that will survive in today’s world is one who can imagine and create their future.
​
For anyone who feels uneasy and boxed up in their careers, or entrepreneurs and organisations who want to stay ahead of the new era, or youths who want to be equipped for the future - we can help you achieve this."
],
"guid":[
[
"Object"
]
],
"industry":[
"Consulting"
],
"link":[
"https://www.myjobmag.com/jobs/business-development-executive-at-protege-management-2"
],
"pubDate":[
"Fri, 18 Nov 2022 17:31:56 GMT"
],
"title":[
"Business Development Executive at Protege Management"
]
},
{
"description":[
"Montaigne Place Is the leading luxury cosmetics, wellbeing and fragrance company in country. We are the hallmark of sophistication, luxury makeup, skincare innovation and sublime fragrances."
],
"guid":[
[
"Object"
]
],
"industry":[
"Sales / Retail"
],
"link":[
"https://www.myjobmag.com/jobs/business-development-manager-female-at-montaigne-ah-limited"
],
"pubDate":[
"Fri, 18 Nov 2022 17:28:02 GMT"
],
"title":[
"Job Openings at Montaigne AH Limited"
]
}]
Here are the logs output below using a searchQuery value of business
LOG filteredObjs []
LOG object
LOG 0
LOG inside 400
LOG searchQuery Business
LOG 3
I saw two errors
Your code:
const filteredObjs = itemListStore.filter(
// here you destructure the row and take item but item doesn't exist in your object
({ item }) => {
// here there is no return
(JSON.stringify(item?.description[0]))?.toLowerCase().includes(searchQuery.toLowerCase()) ||
(JSON.stringify(item?.title[0]))?.toLowerCase().includes(searchQuery.toLowerCase()) ||
( JSON.stringify(item?.link[0]))?.toLowerCase().includes(searchQuery.toLowerCase())
})
You need to do
const filteredObjs = itemListStore.filter(
(item) => {
return (JSON.stringify(item?.description[0]))?.toLowerCase().includes(searchQuery.toLowerCase()) ||
(JSON.stringify(item?.title[0]))?.toLowerCase().includes(searchQuery.toLowerCase()) ||
(JSON.stringify(item?.link[0]))?.toLowerCase().includes(searchQuery.toLowerCase())
})
Seems like you are missing the return statement in your filter callback function. You have to return the boolean result for it to work
const filteredObjs = itemListStore.filter(
// eslint-disable-next-line array-callback-return
({ item }) => {
return (JSON.stringify(item?.description[0]))?.toLowerCase().includes(searchQuery.toLowerCase()) ||
(JSON.stringify(item?.title[0]))?.toLowerCase().includes(searchQuery.toLowerCase()) ||
( JSON.stringify(item?.link[0]))?.toLowerCase().includes(searchQuery.toLowerCase())
})

"react" Each child in a list should have a unique "key" prop

An error seems to occur because the key value is not entered in the map function, but I do not know how to modify the code.
The array is structured like this:
const tabContArr=[
{
tabTitle:(
<span className={activeIndex===0 ? "is-active" : ""} onClick={()=>tabClickHandler(0)}>0</span>
),
},
{
tabTitle:(
<span className={activeIndex===1 ? "is-active" : ""} onClick={()=>tabClickHandler(1)}>1</span>
),
},
{
tabTitle:(
<span className={activeIndex===2 ? "is-active" : ""} onClick={()=>tabClickHandler(2)}>2</span>
),
},
{
tabTitle:(
<span className={activeIndex===3 ? "is-active" : ""} onClick={()=>tabClickHandler(3)}>3</span>
),
}
];
An error occurs in the map function part.
{tabContArr.map((section)=>{
return section.tabTitle
})}
Try with React Fragments with a key attribute as mentioned in React docs
{tabContArr.map((section, index)=>{
return <React.Fragment key={`section-tab-${index}`}>{section.tabTitle}</React.Fragment>
})}
What you have done is not the right way.
If you have data, instead of passing the ReactElement into the array you should pass it into the map function like this:
{tabContArr.map((tab, index)=>{
return <span
className={activeIndex === index ? "is-active" : ""}
onClick={()=>tabClickHandler(index)}
key={`tab-${index}`}>index</span>
})}

key value mapping with array object

I am stuck at mapping value with object.
I have Array of objects
const data = [
{
_id: "535345345345345345311",
name: "Activity",
description: "this is activity",
filters: []
},
{
_id: "535345345345345345311",
name: "Activity",
description: "this is activity",
filters: [
{ _id: "49823476237423984", name: "ACTIVITY filter", origin: "ACTIVITY" }
]
}
];
Here, like that array of object have user_of then it should show User Reading data from the object. Or if data is not present at the object then show as it is. Like if there's data example_of its there in that above array object, but not in object then show example_of only....
{
"user_of": "User",
"student_of": "Student",
"test_of": "TEST"
}
Where this is the object, now if data has user_of I want to show as User in the result output. Similarly if I have student_of in data then I want to show Student
Below is the code where I am trying to achieve this.
{data.filters.map((filter, index) => (
<>
{index < 2 && (
<span style={{ textTransform: "capitalize" }}>
{filter.origin.toLowerCase()}
{index === 0 && record.filters.length > 1 && <span>, </span>}
</span>
)}
</>
))}
{data.filters.length > 2 && (
<span> +{record.filters.length - 2} </span>
)}
also Here's working example of the code https://codesandbox.io/s/hardcore-kilby-hhbon?file=/index.js
What I am getting currently is below
And here's what I expect is
Basically all I want is replace the value with that object value(for naming convention purpose). You can see this on codesandbox link above, I am confused. (New to JS world, Previously with python)
Can someone help me?
You're pretty close:
...
<>
{index < 2 && (
<span style={{ textTransform: "capitalize" }}>
{objet[filter.origin]}
{index === 0 && record.filters.length > 1 && <span>, </span>}
</span>
)}
</>
...
I just changed this to return {objet[filter.origin]}
objet is already defined but you need to add activity_of
const objet = {
user_of: "User",
student_of: "Student",
test_of: "TEST",
activity_of: "Activity"
};

Mapping a different icon depending on property value in React

https://codesandbox.io/s/r546o8v0kq
My above sandbox shows basic mapping of an array of items. This forms a list of notes, dates and an icon depending on what type of item it is.
I am working some logic that maps each item to find out what value it is, based on that I assign the value a string to complete the type of font awesome logo.
const noteType = _.uniq(notes.map(value => value.intelType));
const noteIcon = [
`${noteType}`.toUpperCase() == "EDUCATION"
? "paper-plane"
: `${noteType}`.toUpperCase() == "ELIGIBILITY"
? "heart"
: `${noteType}`.toUpperCase() == "GENERAL"
? "twitter"
: null
];
If "intelType" has a value of "education" it would return the "paper-plane" string to complete the icon. e.g fa fa-${noteIcon}
<List>
{notes.map((note, index) =>
note !== "" ? (
<React.Fragment>
<ListItem className="pl-0">
<i class={`fa fa-${noteIcon}`} aria-hidden="true" />
<ListItemText
secondary={moment(note.createdAt).format("DD-MMM-YYYY")}
/>
</ListItem>
<p>{note.note}</p>
<Divider />
</React.Fragment>
) : null
)}
</List>
Its not getting mapped and returning all three values, which does not meet any criteria therefore returns null as requested. I'm a bit stuck as what to do next here.
You can define an object that maps intel type to icon names:
const noteIconMap =
{ "EDUCATION": "paper-plane",
"ELIGIBILITY": "heart",
"GENERAL": "twitter",
};
And look it up this way inside the render:
<i class={`fa fa-${noteIconMap[note.intelType.toUpperCase()]}`} aria-hidden="true" />
Although, beware, if there is a case where note can have intelType undefined, toUpperCase call will throw an error.
Here's a link to working modified sandbox:
https://codesandbox.io/s/ojz2lzz03z
I'm not sure what's going on with this bit of code:
const noteIcon = [
`${noteType}`.toUpperCase() == "EDUCATION"
? "paper-plane"
: `${noteType}`.toUpperCase() == "ELIGIBILITY"
? "heart"
: `${noteType}`.toUpperCase() == "GENERAL"
? "twitter"
: null
]
But it makes more sense to me to store that information as an object and then access the icon type that way.
const icons = {
EDUCATION: 'paper-plane',
ELIGIBILITY: 'heart',
GENERAL: 'twitter'
}
And then going:
icons[noteType]
You need to get an icon in the map for each note.intelType. Since you're passing an array of ids, none of the icons is matched, and the result is always null.
A simple solution is to create a Map of types to icons (iconsMap), and get the icon from the Map using note.intelType.
btw - currently note.intelType us always uppercase, so you don't need to transform it.
sandbox
const iconsMap = new Map([
['EDUCATION', 'paper-plane'],
['ELIGIBILITY', 'heart'],
['GENERAL', 'twitter'],
]);
class Notes extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
const { notes, classes } = this.props;
return (
<React.Fragment>
<List>
{notes.map((note, index) =>
note !== "" ? (
<React.Fragment>
<ListItem className="pl-0">
<i class={`fa fa-${iconsMap.get(note.intelType)}`} aria-hidden="true" />
<ListItemText
secondary={moment(note.createdAt).format("DD-MMM-YYYY")}
/>
</ListItem>
<p>{note.note}</p>
<Divider />
</React.Fragment>
) : null
)}
</List>
</React.Fragment>
);
}
}

Categories

Resources