I need to update some data dynamically which are in array without appending new entry
constructor(props) {
super(props);
this.state = {
data: [],
isMounted: false
}
}
componentDidMount() {
this.setState({ isMounted: true })
}
componentWillUnmount() {
this.setState({ isMounted: false })
}
updateData(msg) {
if(this.state.isMounted){
this.setState(
update(this.state, { data: { $push: [msg] } }),
);
}
}
// updateDate method will trigger dynamically
Related
There's already some people asking this question but they are almost caused by the same reason (fire the callback in situ e.g. <div onClick={this.handleClick()}></div>). However this doesn't happen to me but I got the same error.
P.S. Some util functions are defined behind the scene.
// Parent
export default class SearchArea extends React.Component {
constructor(props) {
super(props);
this.state = {
data: {
update: '',
confirmed: '',
recovered: '',
deaths: ''
},
showDashboard: false
}
this.setCountryData = this.setCountryData.bind(this);
this.showDashboard = this.showDashboard.bind(this);
}
setCountryData(data) {
console.log(data);
this.setState({
data: {
update: data.lastUpdate,
confirmed: data.confirmed,
recovered: data.recovered,
deaths: data.deaths
}
});
}
showDashboard(toggle) {
this.setState({ showDashboard: toggle })
}
render() {
return (
<div>
<Dropdown onSetCountryData={this.setCountryData} onShowDashboard={this.showDashboard} />
<Dashboard data={this.state.data} visible={this.state.showDashboard} />
</div>
)
}
}
// Sibling1
class Dropdown extends React.Component {
constructor(props) {
super(props);
this.state = {
countries: [],
currentCountry: ''
};
this.handleClick = this.handleClick.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleClick() {
const showCountryData = Fetch(newFetchCountry).showJSONData;
showCountryData().then(res => {
const data = res[0];
this.passCountryData(data);
})
this.passToggleDashboard(true);
}
handleChange(e) {
this.setState({ currentCountry: e.target.value, });
this.passToggleDashboard(false);
}
passCountryData(data) {
this.props.onSetCountryData(data);
}
passToggleDashboard(toggle) {
this.props.onShowDashboard(toggle);
}
componentDidMount() {
let timer = setTimeout(() => {
const showCountryList = Fetch(fetchCountryList).showJSONData;
showCountryList().then(res => {
const data = res.map(country => country);
this.setState({ countries: data })
});
clearTimeout(timer);
}, 2000)
}
render() {
return (
<section className="dropdown-area">
<div className="dropdown">
<label htmlFor="country">Select country:</label>
<input list="countries" name="country" id="country" onChange={this.handleChange} />
<datalist id="countries" required>
{this.state.countries.length ?
this.state.countries.map(country => <option key={country.name}>{country.name}</option>) :
<option disabled>Loading</option>}
</datalist>
</div>
<button className="comp" onClick={this.handleClick}>Search</button>
</section>
)
}
}
// Sibling2
class Dashboard extends React.Component {
constructor(props) {
super(props);
this.state = {
data: {
update: '',
confirmed: '',
recovered: '',
deaths: ''
}
};
}
componentDidMount() {
if (!('data' in this.props)) {
const showWorldData = Fetch(fetchWorld).showJSONData;
showWorldData().then(res => {
const data = res[0];
this.setState({
data: {
update: data.lastUpdate,
confirmed: data.confirmed,
recovered: data.recovered,
deaths: data.deaths
}
});
});
}
}
componentDidUpdate() { // Error here!!!
if ('data' in this.props) {
const data = this.props.data;
this.setState({
data: {
update: data.lastUpdate,
confirmed: data.confirmed,
recovered: data.recovered,
deaths: data.deaths
}
});
}
}
render() {
const visibility = {
visibility: 'visible' in this.props && !this.props.visible ? 'hidden' : 'visible'
};
return (
<section className="dashboard-area" style={visibility}>
<span>Last Update: {this.state.data.update || 'Loading...'}</span>
<div className="dashboards">
<DashboardItem num={this.state.data.confirmed} type="Confirmed" />
<DashboardItem num={this.state.data.recovered} type="Recovered" />
<DashboardItem num={this.state.data.deaths} type="Deaths" />
</div>
</section>
)
}
}
class DashboardItem extends React.Component {
render() {
return (
<div className="dashboard">{this.props.type}: <br /><span>{this.props.num || 'Loading...'}</span></div>
)
}
}
Error is in the componentDidMount() in the Dashboard component. I can't find where I fired the re-render infinitely.
The setState method is repeatedly updating the component because every time the 'data' in this.props equals to true you're calling setState and calling setState will by default update the component and componentDidUpdate will check again if 'data' in this.props equals to true and so
You should make strict conditions for if statement
try this
componentDidUpdate(prevProps, prevState) {
if ('data' in this.props && this.props.data !== prevProps.data) {
const data = this.props.data;
this.setState({
data: {
update: data.lastUpdate,
confirmed: data.confirmed,
recovered: data.recovered,
deaths: data.deaths
}
});
}
}
Your issue stems from derived state: state which is made dependent on props and is an anti-pattern in react.
This will tell you more about it:
https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html
There are some work arounds, but its recommended you instead restructure your data flow.
I have been working on react native project and I ran into an issue.
here's my parent class:
class Home extends Component {
constructor(props) {
super(props);
this.handler = this.handler.bind(this);
}
state = {
userDocExists: null
};
handler() {
console.log("in handler");
this.setState({
userDocExists: true
});
}
render() {
return <UserInfo handler={this.handler} />;
}
}
my child class:
class UserInfo extends Component {
constructor(props) {
super(props);
}
registerUser = () => {
const userId = firebase.auth().currentUser.uid;
firestore
.collection("Users")
.doc(userId)
.set({
firstName: this.state.firstName,
houseNumber: this.state.houseNumber,
CommunityId: this.state.CommunityId,
})
.then((docRef) => {
this.setState({ isLoading: false });
this.props.handler; //not being called
})
.catch((error) => {
console.error("Error adding document: ", error);
});
} else {
this.setState({ ErrorMes: true });
}
};
My this.props.handler is not being called for some reason. but when I use it in an OnPress of a button in the render , it works fine. Could someone tell me why this behaviour occurs?
this.props.handler will give you function definition but to call it You have to invoke it this.props.handler() this trigger the function handler of the parent
class UserInfo extends Component {
constructor(props) {
super(props);
}
registerUser = () => {
const userId = firebase.auth().currentUser.uid;
firestore
.collection("Users")
.doc(userId)
.set({
firstName: this.state.firstName,
houseNumber: this.state.houseNumber,
CommunityId: this.state.CommunityId,
})
.then((docRef) => {
this.setState({ isLoading: false });
//this.props.handler;
this.props.handler(); //Invoked the function
})
.catch((error) => {
console.error("Error adding document: ", error);
});
} else {
this.setState({ ErrorMes: true });
}
I get values from database and save it in state enteredEvent:
class FormEditPage extends Component {
constructor(props) {
super(props);
this.state = {
enteredEvent: {
name: '',
date: new Date(),
time: '',
place: '',
datepub: new Date()
},
};
...
}
componentDidMount() {
this.onHandleEventOneFetch(idEvent);
}
handleChangeInputName(newValue) {
this.setState({ enteredEvent: { name: newValue } });
}
handleChangeInputDate(newValue) {
this.setState({ enteredEvent: { date: newValue } });
}
handleChangeInputTime(newValue) {
this.setState({ enteredEvent: { time: newValue } });
}
handleChangeInputPlace(newValue) {
this.setState({ enteredEvent: { place: newValue } });
}
handleChangeInputDatepub(newValue) {
this.setState({ enteredEvent: { datepub: newValue } });
}
onHandleEventOneFetch(id) {
fetch(..., {
method: 'GET'
})
...
.then(data =>
this.setState({
enteredEvent: {
name: data[0].name,
date: new Date(data[0].date),
time: data[0].time,
place: data[0].place,
datepub: new Date(data[0].datepub)
}
})
);
}
render() {
return (
<div>
<FormEvent
enteredEvent={this.state.enteredEvent}
onHandleChangeInputName={this.handleChangeInputName}
onHandleChangeInputDate={this.handleChangeInputDate}
onHandleChangeInputTime={this.handleChangeInputTime}
onHandleChangeInputPlace={this.handleChangeInputPlace}
onHandleChangeInputDatepub={this.handleChangeInputDatepub}
/>
</div>
);
}
}
In this component I added datapicker and timepicker:
import DatePicker from 'react-date-picker';
import TimePicker from 'react-time-picker';
class FormEvent extends Component {
constructor(props) {
super(props);
this.handleNameChange = this.handleNameChange.bind(this);
this.handleDateChange = this.handleDateChange.bind(this);
this.handleTimeChange = this.handleTimeChange.bind(this);
this.handlePlaceChange = this.handlePlaceChange.bind(this);
this.handleDatepubChange = this.handleDatepubChange.bind(this);
}
handleNameChange(event) {
this.props.onHandleChangeInputName(event.target.value);
}
handleDateChange(newDate) {
this.props.onHandleChangeInputDate(newDate);
}
handleTimeChange(newTime) {
this.props.onHandleChangeInputTime(newTime);
}
handlePlaceChange(event) {
this.props.onHandleChangeInputPlace(event.target.value);
}
handleDatepubChange(newDatepub) {
this.props.onHandleChangeInputDatepub(newDatepub);
}
render() {
return (
<div>
<input type='text' required value={this.props.enteredEvent.name} onChange={this.handleNameChange}/>
<DatePicker onChange={this.handleDateChange} value={this.props.enteredEvent.date}/>
<TimePicker onChange={this.handleTimeChange} value={this.props.enteredEvent.time}
<input type='text' value={this.props.enteredEvent.place} onChange={this.handlePlaceChange}/>
<DatePicker onChange={this.handleDatepubChange} value={this.props.enteredEvent.datepub}/>
</div>
);
}
FormEvent.propTypes = {
enteredEvent: PropTypes.object,
onHandleChangeInputName: PropTypes.func,
onHandleChangeInputDate: PropTypes.func,
onHandleChangeInputTime: PropTypes.func,
onHandleChangeInputPlace: PropTypes.func,
onHandleChangeInputDatepub: PropTypes.func
};
}
In result all datepickers and timepicker get values from enteredEvent. When I change value in one of datepicker/timepicker, values in other datepickers and timepicker became null. How can I fix it?
You've made your state a nested object, and when you set state, you're overwriting the whole object. You will either need to merge the object with the previous state, or stop using a nested object. React will do a shallow merge of state, but not a deep merge.
Doing the merge yourself would look like this, if you can use object spread syntax:
this.setState(oldState => ({
enteredEvent: {
...oldState.enteredEvent,
name: newValue
}
});
If object spread syntax is not at your disposal, then the same thing can be done like this:
this.setState(oldState => ({
enteredEvent: Object.assign({}, oldState.enteredEvent, {name: newValue})
});
If instead you want to go with the approach of flattening the state, it would look like this:
this.state = {
name: '',
date: new Date(),
time: '',
place: '',
datepub: new Date()
};
// ...
this.setState({ name: newValue });
When you have a nested object in state you must make sure to create a copy of the object currently in state or it will be overwritten with a new object with just the given property.
Example
handleChangeInputName(newValue) {
this.setState(previousState => ({
enteredEvent: { ...previousState.enteredEvent, name: newValue }
}));
}
I am trying to close a modal when a user presses outside of the Modal element. Somehow when Dismiss() is called, the state is still the same in the callback.
Why is this happening?
export default class Message extends React.Component {
constructor(props) {
super(props);
this.state = {
id: "",
show: false
};
}
componentDidMount() {
this.props.onRef(this);
}
Show(id) {
this.setState({
id: id,
show: true
});
}
Dismiss() {
this.setState({
id: '',
show: false
}, function (state) {
console.log(state) // undefined
});
}
render() {
if (this.state.show) {
return (
<Modal close={() => this.Dismiss()}>
<h1>{this.state.id}</h1>
</Modal>
);
} else {
return null
}
}
}
Not sure why there's a state argument in your callback, should just be
Dismiss() {
this.setState({
id: '',
show: false
}, function () {
console.log(this.state)
});
}
Yes, that is because this.setState function in React is async. And the new state is only available in the event queue
Here is what I mean:
this.setState({newAdded: "test"});
let youCannotGetIt = this.state.newAdded; // here is undefined, because this.setSate is async
This is the code. No idea as to why there is a problem.
class TeacherForm extends Component {
constructor({ data }) {
super();
this.isUpdatingForm = !! data;
this.state = Object.assign({ ... });
this.handleSubmit = this.handleSubmit.bind(this);
this.removeTeacher = this.removeTeacher.bind(this);
}
handleChange(value, field) {
this.setState({ shouldUpdate: true, [field]: value });
}
handleSubmit(e) {
e.preventDefault();
const { name, subjects, parttime, timing } = this.state;
if (this.isUpdatingForm) {
return update.call({
_id: this.props.data._id,
transaction: { name, subjects, parttime, timing },
}, () => this.setState({ shouldUpdate: false }));
}
return add.call();
}
removeTeacher() {
return remove.call(this.props.data._id);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
...
</form>
);
}
}
The error gets thrown at the handleSubmit method in the callback of update.call. This normally shows up when I call removeTeacher and a list updates and this component unmounts.
It sounds like the callback () => this.setState({ shouldUpdate: false }) is executed after that the component is unmounted. Is that possible? If so, one way to get around that is to replace this part by
return update.call({
_id: this.props.data._id,
transaction: { name, subjects, parttime, timing },
}, () => { !this.unmounted && this.setState({ shouldUpdate: false }); });
and to add
componentWillUnmount() {
this.unmounted = true;
}