Rendering .map of object to change its properties - reactjs

I'm new to React js & React Native please help
so i have this data of partners
const [lessorPartners , setLessorPartners] = useState(null)
const dataPartnerBeforeFetch = [
{id:1, name:"BFI" , img:"lessor1" , code:"PU77"},
{id:2, name:"SMF" , img:"lessor2" , code:"TT38"},
{id:3, name:"Adira" , img:"lessor3" , code:"PT74"},
{id:4, name:"BFI" , img:"lessor1" , code:"PB63"},
{id:5, name:"SMF" , img:"lessor2" , code:"BU42"},
{id:6, name:"Adira" , img:"lessor3" , code:"AL39"}
]
useEffect(() =>{
if(dataPartnerBeforeFetch ){
dataPartnerBeforeFetch.map(dataPartner=>{
dataPartner.color = false
})
setLessorPartners(dataPartnerBeforeFetch)
}
},[dataPartnerBeforeFetch ])
I added color to its end if its true then it will turn transparent / false it will be orange
and I tried to loop it :
and render it all with these functions
const renderingPartners = () => {
return(
lessorPartners.map(lessorPartner => {
renderingPartner(lessorPartner)
})
)
}
const renderingPartner = (lessorPartner) =>{
return(
<div style={{backgroundColor: false ? 'orange' : 'transparent'}}
onClick={()=>{
onClickParter(lessorPartner);
}}
>
<LessorPartner
key = {lessorPartner.id}
object = {lessorPartner}
/>
</div>
)
}
and i tried to call renderingPartners() in my app .js like this
<div>
{ lessorPartners && renderingPartners()}
</div>
but no component returned, just empty and no error
the next idea is to change it's color on click with this function and re render the whole mapping
const onClickParter = (q) =>{
q.color = !q.color
let index = lessorPartners.indexOf(q);
lessorPartners[index]= q
setLessorPartners(lessorPartners)
renderingPartners()
}
just like radio button with list of lessor that i've tried to map
please help i've been stuck here for hours

So map function returns a new array (also it returns a value so if you are specifying braces you have to explicitly write return). you should modify your first snippet like this:
let _dataPartnerBeforeFetch = dataPartnerBeforeFetch.map(dataPartner=>{
return {
...dataPartner,
color : false
}
});
setLessorPartners(_dataPartnerBeforeFetch)
Similarly this snippet should be corrected like this:
lessorPartners.map(lessorPartner => renderingPartner(lessorPartner))
Edited:
Why the color change is not reflected?
Its almost always best to return a new array.
const onClickParter = (q) =>{
let _lessorPartners = lessorPartners.filter(f=> !(f.indexOf(q) >= 0));
_lessorPartners.push({
...q,
color : !q.color
});
setLessorPartners(_lessorPartners);
//renderingPartners();
//we donot have to explicity call the function to enforce rerender because it's already binded by a state variable. So setting the state would do the trick.
}
Here filter returns a new copy of the array without the item in scope (reffered to as 'q'). Then you add a new object with the inverted color to the new array and set the state.

Related

display array values into select option tag in react js

I am trying to display array values into select tag, but all the array values displayed as single value. Please see below logic I added. Data is dynamic, I am getting data from backend like this ["Sankranti", "Sankranti 1"].
const [eventNameList, setEventNameList] = useState([])
var eventList = eventNameList.length > 0 ?
eventNameList.map((item,i) => {
console.log('list: ', item)
return (
<option>{item}</option>
)
})
:
'No Events'
<select>
{eventList}
</select>
please find below console screen shot
It looks that your list is nested inside another array so to fix this you could use flatMap instead of map
or you could just iterate throw the first element inside your nested array
const [eventNameList, setEventNameList] = useState([])
var eventList = eventNameList.length > 0 ?
eventNameList[0].map((item,i) => {
console.log('list: ', item)
return (
<option>{item}</option>
)
})
:
'No Events'
<select>
{eventList}
</select>

Wrapping an an html anchor tag around an array is causing unexpected results in my React component

I have a component that I use in my react app that generates a random game on the screen.
It's working, but now I'm trying to add some html into the game title.
When I do that, my game titles come up as:
[object Object]
Here is where I generate a random game:
const newGame = () => {
return {
title: gameTitleArray[Math.floor(Math.random() * gameTitleArray.length)],
type: gameTypeArray[Math.floor(Math.random() * gameTypeArray.length)],
startDate: getDate(new Date(2019, 0, 1), new Date()),
endDate: getDate(new Date(2022, 0, 1), new Date()),
}
}
You can see I'm trying to wrap an html anchor tag around the 'title' portion.
Here is how I'm exporting the component:
export default function makeGameData(...lens) {
const makeGameDataLevel = (depth = 0) => {
const len = lens[depth]
return range(len).map(d => {
return {
...newGame(),
subRows: lens[depth + 1] ? makeGameDataLevel(depth + 1) : undefined,
}
})
}
return makeGameDataLevel()
}
Here is an example of the gameTypeArray:
const gameTypeArray = ['RPG', 'Western', 'Real-Time Strategy', 'Fantasy', 'First Person Shooter']
And an example of gameTitleArray:
const gameTitleArray = ['Future Agent','Human Universe','Chase of Resitution','Destroy of Resitution','Days and Glitch','Mayhem and Faith','Dynaworks','Crystalback','Fusionheart','Hellscape']
I even tried creating a separate function like this:
function gameTitleArrayLink() {
const gameTitleArray = ['Future Agent','Human Universe','Chase of Resitution','Destroy of Resitution','Days and Glitch','Mayhem and Faith','Dynaworks','Crystalback','Fusionheart','Hellscape']
const title = gameTitleArray[Math.floor(Math.random() * gameTitleArray.length)]
const titleUrl = {title}
return <div dangerouslySetInnerHTML={{ __html: titleUrl }} />
}
And then setting the title like this:
title: researchSummaryList()
Obviously I'm doing something wrong, but I'm not getting any errors, just the [object Object]
Is there anything I'm doing wrong?
Thanks!
Try wrapping the array value in {} to have it treated as an expression:
{gameTitleArray[Math.floor(Math.random() * gameTitleArray.length)]}
I think that the item you want to render
(gameTitleArray[Math.floor(Math.random() * gameTitleArray.length)])
is an object and not a string

Creating an array of styles in React

For certain reasons I need to create an array of different styles to eventually use at certain times. Regardless I have this bit of code...
export const carouselData = {
cdata: [{
bgimage: require('Assets/img/Banners/mybanner1.jpg')
},{
bgimage: require('Assets/img/Banners/mybanner2.jpg'),
}]
}
...
var mySectionStyle
this.props.cdata.cdata.map((carouselData, key) => (
mySectionStyle[key] = {
backgroundImage: "url(" + carouselData.bgimage + ")"
}
))
return (
{ this.props.cdata.cdata.map((carouselData, key) => (
<div className="bg_image" style={ sectionStyle[key] }>
//Some stuff here
</div>
))}
)
Now to anyone that is half decent at coding probably sees huge issues with this code but as a newbie I need help finishing it (or rewriting).
Can anyone help me create an array so I can access my styles one by one with mySectionStyle[0], mySectionStyle[1], mySectionStyle[2] etc
Edit. I have an array that has many images in it and I want those in an array so I can set the carousel up with different background images.
Why can't you just do:
var mySectionStyle = {
style1: {
margin: 0,
},
style2: {
margin: 10,
},
}
const style1 = mySectionStyle['style1'];
const style2 = mySectionStyle['style2'];
If you later need it in an array, you can use the Object methods to convert it.
const availableStyles = Object.keys(mySectionStyle); // ['style1', 'style2']
availableStyles.forEach(style => mySectionStyle[style].backgroundImage = `url(${carouselData.bgimage})`;);
See also Object.values and Object.entries for other conversion to array options.

Vue.js: Manipulate Array and post form with new data

In my Vue.js application I want to post form data to my Node.js/MongoDB Backend.
This is my source code: https://github.com/markusdanek/t2w-vue/blob/master/src/components/backend/JobEdit.vue
JSON for my job entry: http://t2w-api.herokuapp.com/jobs/591c09a55ba85d0400e5eb61
Relevant code for my question:
HTML:
<div class="row">
<input type='text'
:name="'qual'+index"
v-model="qualifications[index]">
<button #click.prevent="removeQualifiaction(index)">X</button>
</div>
Methods:
onChange(value, $event){
if (!this.job.xmlOnline)
this.job.xmlOnline = []
const index = this.job.xmlOnline.findIndex(v => v == value)
const checked = $event.target.checked
if (checked && index < 0)
this.job.xmlOnline.push(value)
if (!checked && index >= 0)
this.job.xmlOnline.splice(index, 1)
}
removeQualifiaction() {
this.qualifications.splice(this.qualifications.index, 1);
}
Sending the form data with submit button on form end:
editJob() {
let job = Object.assign({}, this.job);
job.qualifications = this.qualifications;
job.responsibility = this.responsibility;
this.$http.post('https://t2w-api.herokuapp.com/jobs/' + this.$route.params.id, job).then(response => {
console.log(response);
}, response => {
console.log(response);
});
}
My problems now:
When I edit a "Job", I have a list of "qualification items", that are input fields in my form.
Clicking the "delete" button results that the first input gets deleted, not the one I am clicking. Done with #thanksd answer.
How do I add a button and method to add a new input field and to append it to my job.qualifications?
In my JobAdd.vue implemented, to add a new entry to job.qualifications, like this:
<a #click.prevent="addQualification">+</a>
addQualification() {
this.qualification.push({ text: '' });
}
addJob() {
let job = Object.assign({}, this.job);
job.qualifications = this.qualification.map(q => q.text);
this.$http.post('https://t2w-api.herokuapp.com/jobs/', job).then(response => {....
Full source for my JobAdd.vue: https://github.com/markusdanek/t2w-vue/blob/master/src/components/backend/JobAdd.vue
this.qualification.push({ text: '' }); doesnt work obviously not in my JobEdit.vue when there are already strings in my job.qualifications.
Change your removeQualifiaction method to use the index being passed in:
removeQualifiaction(index) {
this.qualifications.splice(index, 1);
}

React multiple input (array of inputs) not working

So I'm attempting to render multiple input fields with React.
Everything looks fine until I remove an item. Always the last item is being "removed". If you want to try my code, write "A" in input field 1, "B" in 2, "C" in 3 and remove "B". You'll notice that you have removed "C" instead.
I have tried both value and defaultValue for input to no avail. I have also tried giving a name to the input. I think I am missing a key point here.
Any recommendations?
var MultiInput = React.createClass({
getInitialState: function() {
value = this.props.value
// force at least one element
if (!value || value == '') {
value = [ null ]
}
return {
value: value
}
},
getDefaultProps: function() {
return {
}
},
add_more: function() {
new_val = this.state.value.concat([])
new_val.push(null)
this.setState({ value: new_val })
},
remove_item: function(e, i) {
new_state = this.state.value.concat([])
new_state.splice(i,1)
this.setState({ value: new_state })
},
render: function() {
me = this
// console.log(this.state.value)
lines = this.state.value.map( function(e, i) {
return (
<div key={i}>
<input value={e} />
<button onClick={me.remove_item} >X</button>
</div>
)
})
return (
<div>
{lines}
<button onClick={this.add_more}>Add More</button>
</div>
)
}
})
There are a few things going on here.
To start, you shouldn't use the array index as the key when rendering in an array:
lines = this.state.value.map( function(e, i) {
return (
<div key={i}>
<input value={e} />
<button onClick={me.remove_item} >X</button>
</div>
)
})
The first time through, ["A", "B", "C"] renders:
<div key={0}>
...
</div>
<div key={1}>
...
</div>
<div key={2}>
...
</div>
Then, the second time, once you've removed "B" and left ["A", "C"], it renders the following:
<div key={0}>
...
</div>
<div key={1}>
...
</div>
So, when you removed item at index 1, the item previous at index 2 moves to index 1. You'll want to use some unique value that doesn't change when the position in the array changes.
Second, you should use the empty string instead of null for initialization, and then you'll see that you can't type anything in your inputs. That's because value ensures that an input's value is always whatever you pass it; you'd have to attach an onChange handler to allow the value to be edited.
Changing to defaultValue allows you to type in the box, but when you type, the string in this.state.value doesn't get updated--you'd still need an onChange handler.
Finally, your button has an onClick of this.remove_item, but your remove_item method seems to take the event and index as parameters. However, React will not pass the current index to remove_item; you would need to create a new function that passes the correct params:
onClick={me.remove_item.bind(null, i)}
That said, you really shouldn't call Function#bind inside render as you'll create new functions every time it runs.
Working Code
#BinaryMuse clearly explains why my code above doesn't work: by removing an item from the array and render is called again, the items change position and apparently React's algorithm picks the "wrong changes" because the key we're providing has changed.
I think the simplest way around this is to not remove the item from the array but rather replace it with undefined. The array would keep growing with this solution but I don't think the number of actions would slow this down too much, especially that generating a unique id that doesn't change might involve storing this ID as well.
Here's the working code: (If you wish to optimize it, please check #BinaryMuse's suggestions in the accepted answer. My MultInput uses a custom Input component that is too large to paste here =) )
var MultiInput = React.createClass({
getInitialState: function() {
value = this.props.value
if (!value || value == '') {
value = [ '' ]
}
return {
value: value
}
},
getDefaultProps: function() {
return {
}
},
add_more: function() {
new_val = this.state.value.concat([])
new_val.push('')
this.setState({ value: new_val })
},
remove_item: function(i,e) {
new_state = this.state.value.concat([])
new_state[i] = undefined
this.setState({ value: new_state })
},
render: function() {
me = this
lines = this.state.value.map( function(e, i) {
if (e == undefined) {
return null
}
return (
<div key={i}>
<input defaultValue={e} />
<button onClick={me.remove_item.bind(null, i)} >X</button>
</div>
)
}).filter( function(e) {
return e != undefined
})
return (
<div>
{lines}
<button onClick={this.add_more}>Add More</button>
</div>
)
}
})

Resources