ReactJS: Child Component is not updating even I am passing different values - reactjs

I am new to React.
My child component (SmithchartSeriesDirective) successfully displays the data passed from the server, when the parent component (SimplePanel) is loaded for the first time. On subsequent calls the data received from server changes, it is reflected in the props, but once I bind this data to the child component, it does not reflect the updated data in the component.
I am binding the data in listResult array.
Below is Parent Component SimplePanel
export class SimplePanel extends Component<Props> {
render() {
var reactance: number[] = [];
var resistance: number[] = [];
this.props.data.series.map((anObjectMapped, index) => {
if(index==0)
{
reactance = anObjectMapped.fields[0].values.toArray();
}
else
{
resistance = anObjectMapped.fields[0].values.toArray();
}
});
var resultArr =
{
resistance:0,
reactance:0
};
let listResult =[];
for (let index = 0; index < resistance.length; index++) {
var newObj = Object.create(resultArr);
newObj.resistance = Number(resistance[index]);
newObj.reactance=reactance[index];
listResult.push(newObj);
}
return (<div className='control-pane' style={{ height:'100%', width:'100%', backgroundColor:'#161719' }} >
<div className='col-md-12 control-section' style={{ height:'100%', width:'100%' }}>
<SmithchartComponent id='smith-chart' theme="MaterialDark" legendSettings={{ visible: true, shape: 'Circle' }}>
<Inject services={[SmithchartLegend, TooltipRender]}/>
<SmithchartSeriesCollectionDirective>
<SmithchartSeriesDirective
points= {listResult}
enableAnimation={true}
tooltip={{ visible: true }}
marker={{ shape: 'Circle', visible: true, border: { width: 2 } }}
>
</SmithchartSeriesDirective>
</SmithchartSeriesCollectionDirective>
</SmithchartComponent>
</div>
</div>);

welcome to stack overflow.
First remember that arrays saved by reference in JavaScript. So if you change any array by push() or pop() methods, reference to that array doesn't change and React can't distinguish any change in your array (to re-render your component).
let a = [2];
let b = a;
b.push(4);
a == b; //a is [2] and b is [2,4] but the result is true.
You can use this approach as a solution to this problem:
let listResult = [...oldListResult, newObj]; // ES6 spread operator
Also consider for rendering array elements you need to use key prop, so React can render your components properly. more info can be found here.

Related

Rendering .map of object to change its properties

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.

Items in array are returned in only one JSX element using .map()

New to react and still learning. I have an assignment to create a filter component with three dropsdowns that takes information from a JSON file. The idea is the results in the second dropdown will be filtered once the first dropdown has a selected value. The JSON format is:
"destinations": [
{
"id": 8375,
"name": "Bordeaux",
"country": "France",
"category": "wine"
}, ETC
"seasonCategories": {
"spring": [
"wine",
"wonder",
"forest",
"adventure",
"food"
], ETC
I've created a function that feeds the data into the dropdown component and filters it, but it's not returning as I expect: it's creating only one JSX <option> element with the values of all array items listed inside. I need it to generate a new JSX element with the current value of every item in the array. If I call {el[index]} on the last map function I get the right value, so I'm lost as to why it's not generating each in their own <option> tag. The function I'm using is:
function funcCategories(src, val) {
return Object.keys(src)
.filter((flag) => {
return flag === val;
})
.map((el) => {
let v = [];
for (let i = 0; i < src[el].length; i++) {
v.push(src[el][i]);
}
return v;
})
.map((el) => {
return <option className="Dropdown__option">{el}</option>;
});
}
My Dropdown component:
import React from 'react';
class Dropdown extends React.Component {
constructor(props) {
super(props);
this.value = '';
}
render() {
return (
<div className="Dropdown__wrapper">
<label className="Dropdown__label">{this.props.label}</label>
<select
className="Dropdown__select"
value={this.props.value}
onChange={this.props.handleSelect}
>
<option className="Dropdown__option">{this.props.label}</option>
{this.props.func}
</select>
</div>
);
}
}
export default Dropdown;
For you visual thinkers, this is what I'm seeing in the window:
Dropdown result of my current code
I figured it out! I was pushing the array object in the .filter() method and not the elements of each. Not the cleanest code but it works:
let keys = Object.keys(src);
let j = [];
for (let i = 0; i < keys.length; i++) {
if (keys[i] === val) {
alert(keys[i] + ' === ' + src[keys[i]].length)
for (let h = 0; h < src[keys[i]].length; h++) {
j.push(src[keys[i]][h]);
}
}
}
return j.map((el) => {
return <option className="Dropdown__option">{el}</option>;
});

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.

make vuejs component wait for the variable to become available

I have an VueJS component that list the contents of the array to the page. runner.availableResources.cores and runner.availableResources.memory come from bus creates usingbusmq npm package. They take awhile to become available, about 15s depending on IO buffer and thus not immediately available when the page renders.
The error is: [Vue warn]: Error in render: "TypeError: Cannot read property 'cores' of undefined"
How can I make Vue keep checking for values to become available?
<template>
<b-col>
<b-table striped hover :items="formatRunners"></b-table>
</b-col>
</template>
<script>
const fileSize = require("filesize");
export default {
name: "RunnersList",
props: {
runners: Array
},
computed: {
formatRunners() {
const runnerItems = [];
for (const runner of this.runners) {
const newItem = {};
newItem.id = runner.id;
newItem.isPublic = runner.marathon.isPublic;
newItem.AvailableCpu = runner.availableResources.cores;
newItem.AvailableMemory = fileSize(runner.availableResources.memory);
runnerItems.push(newItem);
}
return runnerItems;
}
},
data() {
return {};
}
};
</script>
This is not a really aesthetic solution, but here is a quick workaround:
in your template, add this v-if condition:
<b-table v-if="haveResourcesLoaded" striped hover :items="formatRunners"></b-table>
then in your computed properties, add the corresponding one:
haveResourcesLoaded() {
if (this.runners.length > 0) {
return this.runners[0].availableResources !== undefined
}
return false
}
If you need to do it in a better and more controlled way, you should take a look at the documentation, the bus.isOnline() method might be what you're looking for.
It wasn't so much issue with the listing, as it was update function only getting called once in a minute. The final code is for listing runner is bellow.
<template>
<b-col>
<b-table v-if="runnersTable.length > 0" striped hover :items="runnersTable"></b-table>
</b-col>
</template>
<script>
const fileSize = require("filesize");
export default {
name: "RunnersList",
props: {
runners: Array
},
data() {
return {
haveResourcesLoaded: false
};
},
mounted() {},
computed: {
runnersTable() {
const runnerItems = [];
for (const runner of this.runners) {
const newItem = {
id: runner.id,
isPublic: runner.marathon.isPublic,
AvailableCpu: runner.availableResources.cores,
AvailableMemory: fileSize(runner.availableResources.memory)
};
runnerItems.push(newItem);
}
return runnerItems;
}
}
};
</script>

Resources