I'm trying to set the state based on API data. 'own' is a field in my API and it contains the Boolean value "true"... I'm storing the data I'm getting from the API in an object called passedXdayPassObj...
I want to set the value of checked property based on the API value of "own"..But it doesn't happen..
Following is my code...
componentDidUpdate(prevProps) {
let passedXdayPassObj;
const { xDayPass } = this.props;
if (xDayPass.xDay && xDayPass.xDayLoading === false && JSON.stringify(xDayPass.xDay) !== JSON.stringify(prevProps.xDayPass.xDay) && !this.props.pathname.includes("createChannel") && !this.state.isSubmit) {
passedXdayPassObj = {
own: xDayPass.xDay.own,
totalCount: xDayPass.xDay.totalCount,
totalValue: xDayPass.xDay.totalValue,
purchaseStart: moment(xDayPass.xDay.purchaseStart).format(),
purchaseEnd: moment(xDayPass.xDay.purchaseEnd).format(),
price: xDayPass.xDay.price,
restricted: false,
utilizeStart: xDayPass.xDay.utilizeStart,
utilizeEnd: xDayPass.xDay.utilizeEnd,
}
if (this.props.location && this.props.location.state && this.props.location.state.view || this.props.location.state.edit) {
this.props.initialize(passedXdayPassObj);
}
if (passedXdayPassObj && passedXdayPassObj.own) {
this.setState({
checked: passedXdayPassObj.own
});
}
}
}
You don't do state updates inside of compoonentDidUpdate() lifecycle methods it results in too many recursions ultimately crashing your app.
Shift the same code inside of componentDidMount() and everything will work fine.
Related
I have a Datagrid inside a List with custom sorting and filtering. Everything works fine except that when the function that updates the sorting is called, the getList method is called twice (the server is called twice, which is very time consuming).
Here is the fucntion responsible of updating sorting field and order:
const handleSort = (key) => {
console.log("key for sorting :", key);
const {field, order} = sort;
console.log("field and order: ", field +" "+order);
if(field === key) {
sort.order = order === "ASC" ? "DESC" : "ASC";
} else {
sort.field = key;
}
setSort(sort);
refresh();
}
What could probably be the cause of this ?
Edit:
Actually I found that every simple click anywhere on the web page causes react-admin to call getList with the current url (to refresh the page). Is there a way to disable this behaviour or to control it ?
The first request was actually done on windows focus as dictated by React pareameter refetchOnWindowFocus, which has the value true by default.
I changed its value to false in the queryClient prop of the Admin object like the following:
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 10000,
retry: false,
refetchOnWindowFocus: false,
},
},
});
return <Admin
queryClient={queryClient} />
...
</Admin >
I use the function changeCheck to check and uncheck specific components.
When I use the function, it works correctly.
this.props.team is a list of all of the teams.
The goal of changeAllTeams is to be able to check and uncheck all of the teams that have a specific league.
In this example I want to change all of the teams that have a league acronym of NFL:
this.state = {
checked: [],
checkedTeams: [],
teamObject: [],
queryString: [],
accordionStatus: [true, true, true]
}
changeAllTeams = (leagueType) => {
this.props.team.map(
(v, i) => {
if(v.league.acronym === 'NFL'){
this.changeCheck(i, v.team_name, v)
}
}
)
}
componentDidUpdate(){
console.log('checked', this.state.checked)
console.log('team object', this.state.teamObject)
console.log('props team object', this.props.teamObject)
this.props.changeLeagues(this.props.league, this.props.checkedLeagues, this.state.checkedTeams, this.state.queryString, this.state.teamObject, this.state.checked)
}
changeCheck = (index, name, teamObject) => {
//updates checked team state
if(!this.state.checkedTeams.includes(name)){
this.state.checkedTeams[this.state.checkedTeams.length] = name
this.setState({ checkedTeams: [...this.state.checkedTeams] })
//sets team object with new team object
this.state.teamObject[this.state.teamObject.length] = teamObject
this.setState({ teamObject: this.state.teamObject })
} else {
console.log(name)
newChecked = this.state.checkedTeams.filter(v => { return v !== name})
this.setState({ checkedTeams: newChecked })
//removes team object and sets new state
newObjectChecked = this.state.teamObject.filter(v => { return v.team_name !== teamObject.team_name})
this.setState({ teamObject: newObjectChecked })
}
//updates checkbox for specific space
this.state.checked[index] = !this.state.checked[index]
this.setState({ checked: this.state.checked })
this.forceUpdate()
}
When I map over the array in changeAllTeams, only the last object in the array takes effect.
The state for checked updates for everything, but the state for checkedTeams and teamObject does not.
This video may help to understand further:
https://streamable.com/q4mqc
Edit:
This is the structure of the objects in this.props.team:
I don't have your code but I'm pretty sure that the problem is that you didn't provide a unique id for each item (remember that it's most of the time a bad idea to use map index for your items). The thing that you should do is to give each item a unique key and call the function based on that id.
There are a few places where you mutate the contents of this.state. That could cause React to be unable to detect changes in the state because the new and old state are referencing the same object. I would recommend that you don't mutate any state and instead create clones of the data object before passing the new data to setState()
So I have a little bit of form validation going on and I am running into an issue. When I first load the web app up and try adding a value and submitting with my button it doesn't allow me and gives me the error I want to see. However, when I add a value setState occurs and then my value is pushed to UI and I try to add another blank value it works and my conditional logic of checking for an empty string before doesn't not go through what am I doing wrong?
addItem() {
let todo = this.state.input;
let todos = this.state.todos;
let id = this.state.id;
if (this.state.input == '') {
alert("enter a value");
document.getElementById('error').style.color = 'red';
document.getElementById('error').innerHTML = 'Please enter something first';
}
else {
this.setState({
todos: todos.concat(todo),
id: id + 1,
}, () => {
document.getElementById('test').value = '';
})
console.log(this.state.id);
}
}
You are checking this.state.input but no where in that code are you setting the input value on the state.
Try adding this where it makes sense in your application:
this.setState({ input: 'some value' });
Also, I recommend you use the state to define the application UI. So instead of using document.getElementById('error') or document.getElementById('test').value, have the UI reflect what you have in your state.
See here for more info: https://reactjs.org/docs/forms.html
Instead of manipulating the DOM directly:
document.getElementById('test').value = '';
you'll want to use React:
this.setState({ input: '' });
A good ground rule for React is to not manipulate the DOM directly through calls like element.value = value or element.style.color = 'red'. This is what React (& setState) is for. Read more about this on reactjs.org.
Before you look for the solution of your issue, I noticed that you are directly updating the DOM
Examples
document.getElementById('error').style.color = 'red';
document.getElementById('error').innerHTML = 'Please enter something first';
document.getElementById('test').value = '';
Unless you have special use case or dealing with external plugins this isn't recommended, when dealing with React you should update using the virtual DOM. https://www.codecademy.com/articles/react-virtual-dom
Pseudo code sample
constructor(props) {
this.state = {
// retain previous states in here removed for example simplicity
errorString: ''
}
}
addItem() {
let todo = this.state.input;
let todos = this.state.todos;
let id = this.state.id;
if (this.state.input == '') {
alert("enter a value");
this.setState({
errorString: 'Please enter something first'
});
}
else {
this.setState({
todos: todos.concat(todo),
id: id + 1,
input: '',
});
}
}
// notice the "error" and "test" id this could be omitted I just added this for your reference since you mentioned those in your example.
render() {
return (
<div>
{(this.state.errorString !== '') ? <div id="error" style={{color: 'red'}}>{this.state.errorString}</div> : null}
<input id="test" value={this.state.input} />
</div>
}
Every time you invoke setState React will call render with the updated state this is the summary of what is happening but there are lot of things going behind setState including the involvement of Virtual DOM.
I have a scenario where I'm passing data from a reducer into my react state.
data:
{
"id": 1,
"title": "Test",
"content": {
"body": "sdfsdf"
"image": "http://example.com"
}
}
Using componentWillRecieveProps, this works perfectly for retrieving the title.
componentWillReceiveProps(nextProps) {
this.setState({
title: nextProps.blog.title,
})
}
However, I'm having difficulty retrieving the nested fields. When I do this:
componentWillReceiveProps(nextProps) {
console.log("new title is", nextProps.blog.title);
console.log("new body content is", nextProps.blog.content["body"]);
this.setState({
title: nextProps.blog.title,
body: nextProps.blog.content["body"]
})
}
I get this error:
The error of an undefined body goes away after I click the debugger and the content is loaded. Is there anyway I can combat this issue?
I tried to check for undefined like this:
if (typeof nextProps.blog.content["body"] != 'undefined'){
But this doesn't work either and I believe it's because the blog is undefined.
What you can do is check whether you props is defined initially or not by checking if nextProps.blog.content is undefined or not since your body is nested inside it like
componentWillReceiveProps(nextProps) {
if(nextProps.blog.content !== undefined && nextProps.blog.title !== undefined) {
console.log("new title is", nextProps.blog.title);
console.log("new body content is", nextProps.blog.content["body"]);
this.setState({
title: nextProps.blog.title,
body: nextProps.blog.content["body"]
})
}
}
You need not use type to check for undefined, just the strict operator !== which compares the value by their type as well as value
In order to check for undefined, you can also use the typeof operator like
typeof nextProps.blog.content != "undefined"
I was face same problem ..... And I got solution by using typeof()
if (typeof(value) !== 'undefined' && value != null) {
console.log('Not Undefined and Not Null')
} else {
console.log('Undefined or Null')
}
You must have to use typeof() to identified undefined
In case you also need to check if nextProps.blog is not undefined ; you can do that in a single if statement, like this:
if (typeof nextProps.blog !== "undefined" && typeof nextProps.blog.content !== "undefined") {
//
}
And, when an undefined , empty or null value is not expected; you can make it more concise:
if (nextProps.blog && nextProps.blog.content) {
//
}
You can try adding a question mark as below. This worked for me.
componentWillReceiveProps(nextProps) {
this.setState({
title: nextProps?.blog?.title,
body: nextProps?.blog?.content
})
}
You can check undefined object using below code.
ReactObject === 'undefined'
I want to show the difference between a components current and next props. For example:
componentWillReceiveProps(nextProps) {
let diff = someFunction(this.props, nextProps);
console.log(diff);
}
How is this done?
This npm package seemed to work nicely:
https://github.com/Tixit/odiff
You can do a shallow comparison with a function like the following (I'm using ES6):
function compareObjects (objectA, objectB) {
if (objectA === objectB) {
return true // return true if both variables are referencing the same object
}
let key
// check what property does objectA have that objectB has not
for (key in objectA) {
if (objectA.hasOwnProperty(key) && !objectB.hasOwnProperty(key)) {
return false // return false if there's a difference
}
}
// check what property does objectB have that objectA has not
for (key in objectB) {
if (objectB.hasOwnProperty(key) && !objectA.hasOwnProperty(key)) {
return false // return false if there's a difference
}
}
}
Note that this is only shallow checking and does the comparison on first level. It only compares if the objects given consist of the same values (therefore it's called shallow).