I have question about changing properties inside the object array. I will post my code below for better understanding. First, I have object array that contains two array and first array contain attribute called "first" and second array contains "last". When I click on the function it will pass the specific name such as first or last as string. After that I have to loop through the object array and change the value that matches the properties name (tempFirst,tempLast). I just declared inside the onHandle function but on my real project, I am passing through the parameters.
example would be: if (first === first) change the value of that property.
import logo from './logo.svg';
import './App.css';
import react ,{ Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
objectTemp: []
};
}
componentDidMount(){
let objectArray = [
{
name: "hello",
first: 77
},
{
name: "world",
last: 66
},
]
this.setState({
objectTemp: objectArray
})
}
onHandle = () => {
let tempFirst = "first";
let tempLast = "last";
let storeState = [...this.state.objectTemp];
//console.log(storeState);
// here i want to check if either tempfirst or templast is equal to the property on object array
for(let i = 0; i < storeState.length; i++){
//if(storeState[i] === tempLast){
console.log(...storeState[i]);
//}
}
}
render() {
console.log(this.state);
return <button onClick={this.onHandle}>VIEW</button>;
}
}
export default App;
I think the step for your modification is:
onHandle = (firstName: string, value: any) => {
/// Modify your data:
const newData = this.state.objectTemp.map( item => {
/// Update Data if matched condition
if(item.firstName === firstName){
item.value = value
}
return item
})
/// Update state
this.setState({objectTemp: newData})
}
You can map the previous state to new state, and when mapping each element object, check each key against a "searchKey" and if you find a match update the value.
Here's an example onHandle function that maps the objectTemp state and creates an array of object entries (i.e. array of key-value pairs) of the objects. It reduces the entries array back into an object, and while doing so checks the object keys for the match.
onHandle = (searchKey, newValue) => {
this.setState(prevState => ({
// map previous state to next state
// [obj1, obj2, ...objN]
objectTemp: prevState.objectTemp.map(obj => {
// Create array of entries and reduce back into object
// [[key1, value1], [key2, value2], ...[keyN, valueN]]
return Object.entries(obj).reduce((obj, [key]) => {
// If key is a match to search key, create new object and update value,
// otherwise return existing object
return key === searchKey
? {
...obj,
[key]: newValue
}
: obj;
}, obj);
})
}))
}
Demo
Related
I have the following array I am working with
const data = [
{
name: "Person",
id: 0,
familyMembers: [
{
id: 00,
name: "personOne"
},
{
id: 01,
name: "personTwo"
}
]
},
]
I am dynamically mapping over this array with one map for the main objects and another for the nested array. I have a toggle and I am trying to implement functionality that when the parent is selected it automatically selects the nest kids of said parent as well.
const [isSelected, setIsSelected] = useState({});
const handleCheck = (name: string, event: any): void => {
setIsSelected({ ...isSelected, [name]: event.target.isSelected });
};
Well, it took me some time to apply it :)
First of all, there isn't any isSelected prop for the CheckBox component and it should be changed to checked as below:
checked={isSelected[id]}
Then, handleCheck function in dataList was not called properly and it should be changed as below:
dataList(
nestedItem.name,
nestedItem.id,
true,
isSelected,
(name: string, event: any | undefined) =>
handleCheck(event, name)
)
we need to also initialize values in useState otherwise we would receive this error:
component is changing an uncontrolled input to be controlled
Finally, in the handleCheck function I checked whether data[name] exist and then mapped over data[name].familyMembers and stored checked values in the isSelected object as below:
const handleCheck = (name: string, event: any): void => {
var nested = {};
if (data[name]) {
data[name].familyMembers.map(
(el, id) => (nested = { ...nested, [el.id]: event.target.checked })
);
}
setIsSelected({ ...isSelected, [name]: event.target.checked, ...nested });
};
You can also check these corrections online here in sandbox
I am using react hooks to render the data dynamically. I am performing two things:
a. fetching URL parameter from component's history props.
b. fetching state data from component's history props that returns me an array of objects something like:
current_data: Array(2)
0: {registration_data: {…}, company_name: "Test"}
1: {registration_data: {…}, company_name: "Sample"}
lastIndex: (...)
lastItem: (...)
length: 2
__proto__: Array(0)
What I need to achieve is, to render the data out of current_data object, that matched my URL query param. I am using for loop to iterate over my array. I know it's a noob question, but kinda stuck to get desired output.
import React, {useState, useEffect} from 'react';
const ViewRegistrations = (props) => {
const [queryParam, setParam] = useState('');
useEffect(() => {
const queryParam = new URLSearchParams(props.location.search).get('registration');
setParam(queryParam);
})
const [dataRecieved, setData] = useState( [] );
useEffect(() => {
const data = props.location.state.current_data;
// const testData
for(var i = 0; i< data.length; i++){
console.log(data[i] === queryParam)
}
});
return (
<div>
{}
</div>
)
}
export default ViewRegistrations;
The statement console.log(data[i] === queryParam) returns me boolean value everytime. I want the array data matched with query param to be stored in dataRecieved state. Please help to sort this out.
One solution to your question would be as follows
1. First of all get your desired queryParam on the mount of the component (I am also unsure about your usage of the useState hook, the second variable should be the first one with the 'set' added), like so:
const [queryParam, setQueryParam] = useState('');
useEffect(() => {
const query = new URLSearchParams(props.location.search).get('registration');
setQueryParam(query);
}, []) // don't forget to add an empty dependency array so it runs only once
Secondly, you then do your condition of match with another useEffect
useEffect(() => {
const data = props.location.state.current_data;
if (data && queryParam)
for(var i = 0; i< data.length; i++){
data[i] === queryParam ? setDataReceived([...dataReceived, data[i]) : null;
}
}, [queryParam])
And lastly you do whatever you want in terms of displaying them, you could achieve this with map in your component, something like
{dataReceived.length && dataReceived.map((data) => <whatever>data</whatever>)}
I am not 100% sure this is a working version, maybe some tweaks should be made, but this gives you an idea
I'm using react useState, where the state is an object with some nested properties. When I call setState on it, I'm not seeing a re-render or the state being updated. I assume react is seeing that the new state equals the old state and so no updates occur. So, I've tried cloning the state first, but still am not seeing any updates.
How can I get this function to cause the state to update?
export type TermEditorStateRecord = {
term: SolrTermType;
state: SolrTermEditorRecordState;
classifications: { [key: string]: string };
};
export type TermEditorStateRecordMap = {
[phrase: string]: TermEditorStateRecord;
};
const [records, setRecords] = useState({});
const setRecordClassification = (label, key, value) => {
const cloned = new Object(records) as TermEditorStateRecordMap;
cloned[label].classifications[key] = value;
setRecords(cloned);
};
I apologize for the TypeScript types, but I've included them here so that you can see the expected shape of the state.
Is it not updating because the changes are deeply nested? Is there a way to get this to work, or do I need to somehow pull the state that changes out into its own state?
new Object does not make a deep copy, so for setRecords it's the same instance and it won't trigger the re-render,
const obj = {
a: {
b: "b"
}
};
const copy = new Object(obj);
copy["c"] = "c";
console.log(obj);
You'll need to manually updated the nested property :
const setRecordClassification = (label, key, value) => {
setRecords(record => ({
...record,
[label]: {
...record[label],
classifications: {
...record[label].classifications,
[key]: value
}
}
}));
};
or to create a copy, use :
const cloned = JSON.parse(JSON.stringify(record));
cloned[label].classifications[key] = value;
setRecords(cloned);
I have a component that I am giving an array with objects as props to it like this:
describe('component', () => {
it('should return the correct number of items passed in the array', () => {
const comp = shallowMount(component, {propsData: {
buttons: [
{name:'button1'},
{name:'button2'}
]
}});
expect(component.length).toHaveLength(buttons).length
});
});
How can I test that the provided array has the correct length, for example if there are two objects in the array, the component should return two, if there is one, one, if there are none then it should return 0, how can I achieve that? I tried
expect(component.length).toHaveLength(buttons).length
But that does not work
I guess you want to check if the correct number of childs of some type was rendered (in Vue).
// import component that you want to count, e.g. Button
const buttons = [
{ name: 'button1' },
{ name: 'button2' }
]
const comp = shallowMount(component, { propsData: { buttons } })
expect(comp.findAll(Button).length).toBe(buttons.length)
https://lmiller1990.github.io/vue-testing-handbook/finding-elements-and-components.html#findall
const buttons = [
{name:'button1'},
{name:'button2'}
]
const comp = shallowMount(component, {propsData: { buttons }});
expect(comp.findAll(Button)).toHaveLength(buttons.length)
I am using react and I have a component where I am setting a constant with an array of strings:
const propsToCheck = ['text', 'type', 'status', 'topic.name', 'class.0.code'];
I am sending down the children chain all the way to the component that is using it like this in the function checkObjectContainsValue that I import from another file:
constructor(props) {
super(props);
this.state.data = this.transformRows(props.rows.filter(obj => checkObjectContainsValue(obj, props.searchString)(props.propsToCheck)))
}
My tests are failing then because I get an error:
TypeError: propsToCheck is not iterable
But, I noticed that if I import the checkObjectContainsValue function to a parent component where I have propsToCheck I don't get the error. But I can't have that function there for other reasons. I have checked the react developer tools and I can see that I am getting the array propsToCheck in the child component, so I am not sure why I am getting this error?
This is the function checkObjectContainsValue:
const checkObjectContainsValue = (obj, value) => (propsToCheck) => {
for (let prop of propsToCheck) {
const propToCheckValue = prop.split('.').reduce((currentObject,property) => {
return currentObject[property];
}, obj);
if (propToCheckValue.toLowerCase().includes(value.toLowerCase())) {
return true;
}
}
return false;
};
Update
If I move the logic to a parent component then everything works fine. This is the example:
onSearchChange(event) {
const searchString = event.target.value;
this.setState(prevState => {
const filteredTasks = prevState.tasks.filter(obj => checkObjectContainsValue(obj, searchString)(propsToCheck));
return {filteredTasks: filteredTasks};
});
}
Why am I getting this error only when I use this function in the child component?
Your function expects three argument
const checkObjectContainsValue = (obj, value, propsToCheck) => {
where as you're just passing two argument to function
props.rows.filter(obj => checkObjectContainsValue(obj, props.searchString)
so the third argument will have a value equal to undefined which is not iterable