React set state property dynamically - reactjs

I'm using react and I have some methods to set the state of my COmponent separately.
I have the following methods:
setLineColor(value){
this.setState({stroke:value},()=>{
this.props.data(this.getStyleData());
});
}
setFillColor(value){
this.setState({ fill:value},()=>{
this.props.data(this.getStyleData());
});
}
setMode(value){
this.setState({ mode:value},()=>{
this.props.data(this.getStyleData());
});
}
How can I combine the methods, so that I can have something like:
setAttribute(propery,value){...}
?

Like this
setAttribute(property, value) {
this.setState({ [property]: value }, () => {
this.props.data(this.getStyleData());
});
}
Example

Related

React testing error "Cannot read get property of undefined"

I am trying to test the function searchTrigger in my CardMain component.
export default class CardMain extends Component {
state = {
Pools : [],
loading: false,
}
componentDidMount(){
axios.get('/pools')
.then (res => {
//console.log(res.data.data);
this.setState({
Pools: res.data.data,
loading: true,
message: "Loading..."
},()=>{
if (res && isMounted){
this.setState({
loading: false
});
}
})
}
)
.catch(err=>{
console.log(err.message);
})
}
// the function is for search method
// upon search, this function is called and the state of the pools is changed
searchTrigger = (search) => {
Search = search.toLowerCase();
SearchList = this.state.Pools.filter((e)=> {
if (e.name.toLowerCase().includes(Search)){
this.setState({
loading: false
})
return e
}
})
if (SearchList.length === 0){
this.setState({
loading: true,
message: "No pools found"
})
}
}
render() {
return (
<div>
<Searchbar trigger={this.searchTrigger}/>
{ this.state.loading ?
<div className="d-flex justify-content-center">{this.state.message}</div>
:<div>
{Search === "" ? <Card1 pools={this.state.Pools}/> : <Card1 pools={SearchList}/> }
</div>
}
</div>
)
}
}
The function searchTrigger is passed to another class component called Searchbar which basically displays the search bar. Upon searching something, the function searchTrigger is called and the searched value is passed as an argument to this function.
So, I am trying to test this function and I am new to react and testing. I found some examples online and tried a simple testing whether the function is called or not. My CardMain.test.js code looks like this:
describe("callback function test", ()=> {
it("runs it", () => {
//const spy = jest.spyOn(CardMain.prototype,"searchTrigger");
const cardmain = shallow(<CardMain/>)
const spy = jest.spyOn(cardmain.instance(), "searchTrigger");
expect(spy).toHaveBeenCalled()
})
});
I get the TypeError: Cannot read property 'get' of undefined pointing to the axios.get("/pools") in the CardMain component inside componentDidMount. axios is being imported from another component api.js which creates the instance of axios using axios.create. I have no idea what the problem is. I am very new to react. I have absolutely no idea, how do I test these components? Could somebody help me?
Update:
So, i tried mocking axios call:
let Wrapper;
beforeEach(() => {
Wrapper = shallow( <CardMain/>);
});
describe("Card Main", ()=> {
it("returns data when called", done => {
let mock = new MockAdapter(axios);
const data = [{
name: "Test",
response: true
}];
mock.onGet('My_URL')
.reply(200,data);
const instance = Wrapper.instance();
instance.componentDidMount().then(response => {
expect(response).toEqual(data);
done();
});
});
});
It says "cannot read property .then of undefined"

React clone vs object.assign

So in my setState method i have following code that works fine.
onDropoffTimeChange(event) {
event.persist();
this.setState((currentState) => {
const searchParams = currentState.searchParams.clone();
searchParams.dropoffTime = event.target.value;
return { searchParams };
});
}
Clone is a method i put inside SearchParams class like following:
class SearchParams {
constructor(
aDropoffTime,
) {
this.dropoffTime = aDropoffTime;
}
clone() {
return new SearchParams(
this.dropoffTime,
);
}
}
Now it was my understanding that object.assign would do the same thing? In other words i would like to get rid of my clone method and use following but it does not update the state?
onDropoffTimeChange(event) {
event.persist();
this.setState((currentState) => {
const searchParams = Object.assign({}, currentState.searchParams);
searchParams.dropoffTime = event.target.value;
return { searchParams };
});
}
You can simply use '...' operators to get rid of 'clone' method if you are using ES-6.
this.setState((currentState) => {
return {
searchParams: {
...currentState.searchParams,
dropoffTime: event.target.value,
}
};
});
You should use the Object spread method (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) to copy an iterable.
If dropoffTime is part of your searchParams object you will need to spread the currentState.searchParams inside of the searchParams object.
this.setState((currentState) => {
return {
searchParams: {
...currentState.searchParams,
dropoffTime: event.target.value
}
};
});

Possible to do a callback in setState like this in React?

I'm now to react and I'm wondering if what I've done is a bad way of creating this component. What I wonder is:
Is this the correct way to do the callback in the setState? If not, where should this line $('#editor').data('kendoEditor').value(data) be placed?
componentDidUpdate(prevProps) {
if(this.props.id!== prevProps.id) {
$.get('/webapi/GetData?id=' + this.props.id, function (data) {
this.setState({ editorValue: data }, $('#editor').data('kendoEditor').value(data));
}.bind(this));
}
}
Why doesn't this work?
componentDidMount() {
this.initEditor();
$.get('/webapi/GetData', function (data) {
this.setState({ data: data });
}.bind(this));
}
initEditor = () => {
$("#editor").kendoEditor({
value: this.state.editorValue,
)}
}
but this works?
componentDidMount() {
$.get('/webapi/GetData', function (data) {
this.setState({ data: data });
this.initEditor();
}.bind(this));
}
To properly do a callback after setState follow this format:
this.setState( { foo: bar }, () => callbackFunction() )
EDIT
To answer the second part of your question, you shouldn't need to use those lines of code at all. Let React handle the rendering. Say you have a render like so
render() {
return(
<div>
<input type="text" name="someValue" data-kendoeditor={this.state.editorValue} />
</div>
)
}
Then call setState like:
componentDidUpdate(prevProps) {
if(this.props.id!== prevProps.id) {
$.get('/webapi/GetData?id=' + this.props.id, function (data) {
this.setState({ editorValue: data });
}.bind(this));
}
}
This will rerender the value from state to the DOM.

setState is not defined

I have a print function, it first sets the state of isPrinting to true and open a pring dialog. Once the dialog is being closed it sets the state of isPrinting to false and at this point I'm getting the error (second setState):
Uncaught ReferenceError: setState is not defined
I binded function to current context with the arrow function.
handlePrint = () => {
this.setState({ isPrinting: true }, () => { //setState is working working properly
window.print();
if (window.matchMedia) {
var mediaQueryList = window.matchMedia('print');
mediaQueryList.addListener = (mql) => {
if (!mql.matches) {
this.setState({ isPrinting: false }); //Error
}
}
}
});
};
I am not sure what you are trying to achieve here but the window.print() freezes the app. There is no code being run unless someone clicks the printing screen away. I works just like window.alert("..."). You can try that by printing a thimestamp right after the win.print. So besides that there is a problem with the this context that cannot be reached the whole function is useless. Because you could just do:
handlePrint = () => {
this.setState({ isPrinting: true }, () => {
window.print() //freezes until someone clicks it away.
this.setState({ isPrinting: false }) //Error
})
}
Regards
Try this.
handlePrint = () => {
let _this = this;
this.setState({ isPrinting: true }, () => {
window.print();
if (window.matchMedia) {
var mediaQueryList = window.matchMedia('print');
mediaQueryList.addListener = (mql) => {
if (!mql.matches) {
_this.setState({ isPrinting: false });
}
}
}
});
};
This should help
handlePrint = () => {
this.setState({ isPrinting: true }, () => { //setState is working working properly
window.print();
if (window.matchMedia) {
var mediaQueryList = window.matchMedia('print');
mediaQueryList.addListener = (mql) => {
if (!mql.matches) {
return { isPrinting: false };
}
}
}
});
};
setState method should return a new state rather than try to execute anything.
At the second time, instead of setState, just return the new state like:
return {
isPrinting: false,
};
How did you use the function 'mediaQueryList.addListener'?You can console the two 'this' and see if they are same.

Why is my input not updating? - React

I can't figure out why my input is not updating. Here is my code:
state = {
org: {
orgName: ''
}
};
updateInput = field => event => {
this.setState({
[field]: event.target.value
})
}
render() {
let { org } = this.state
return (
<input
value={org.orgName}
onChange={this.updateInput('orgName')}
/>
)
}
I type data into the input. It calls updateInput and sets the state. When render is called, the org.orgNameis '' again. This should be working.
I have even added a log in the setState callback:
this.setState({
[field]: event.target.value
}, () => console.log(this.state.org))
and it logs out the org info that has been entered into the input
What am I missing? How do I make this work?
You have a nested object in your state - you are updating this.state.orgName instead of this.state.org.orgName
updateInput = field => event => {
this.setState({
[field]: event.target.value
})
}
needs to be
updateInput = field => event => {
this.setState({
org: {
...this.state.org,
[field]: event.target.value
}
})
}
Would recommend you avoid nesting objects in state though going forward. Will prove difficult to optimize later on.

Resources