react-native-calendar : selected ON/OFF when date is selected - javascript

I am using react-native-calendar for my project. I can mark on the calendar when I press on a date. But I want to mark off when marked date is pressed again.
This is my function code:
onDayPress = (day) => {
const _selectedDay = Moment(day.dateString).format(_format);
this.setState(({pressedDate}) => ({
pressedDate: {
...pressedDate,
[_selectedDay] : {
selected: true
}
},
selectedDay:_selectedDay
}))
console.log(this.state.pressedDate, 'this.state.pressedDate')
}
In my calendar
<Calendar
style={styles.calendarBox}
markedDates={this.state.pressedDate}
onDayPress={this.onDayPress}
markingType={'multi-dot'}
monthFormat={'yyyy MMMM'}/>
Is there a way I can mark on and off dates? Also, I want only to mark up to three dates. Is this possible?
Also, when I console.log('this.state.pressedDate') I get undefined at first. When I click on it again then I get the value any idea why this is happening?.

Is there a way I can mark on and off dates?
As you can see in Calendar implementation, it only updates when you pass difference current to this component. You can do this trick:
_onDayPress = day => {
const { dateString } = day;
const { markedDates } = this.state;
const isMarkedBefore = !!(
markedDates[`${dateString}`] &&
markedDates[`${dateString}`].selected
);
markedDates[`${dateString}`] = { selected: !isMarkedBefore };
this.setState(
{ ...this.state, markedDates, current: null },
() => {
this.setState({
...this.state,
current: dateString
});
});
};
...
<Calendar
style={styles.calendarBox}
current={this.state.current}
markedDates={this.state.markedDates}
onDayPress={this._onDayPress}
markingType={'multi-dot'}
monthFormat={'yyyy MMMM'}/>
when I console.log('this.state.pressedDate') I get undefined at first. When I click on it again then I get the value any idea why this is happening?.
setState is asynchronous function so if you want to see what your state change after that you should log in second callback parameter like
this.setState({...someState}, () => {
console.log(this.state);
})

Related

How to update state for function that relies on if statement from state?

I have an array called values:
values = RAW_VALUES.map((data) => {
return {
id: data.id,
name: data.name,
value: financials[data.id],
add: false,
subtract: false,
change: "",
newValue: "",
};
});
and a function:
updateValues(val, index) {
let { values } = this.state;
if (!isNaN(val)) {
values[index].change = parseFloat(val);
if(values[index].add){ //this checks if add property is true
values[index].newValue = values[index].value + values[index].change
}
this.setState({
values,
});
}
}
I have a button called 'Add' which toggles the add field of each index in values.
To update values[index].newValue, the user currently needs to set add to true first and then type in the updated value.
How can I ensure that the value is also updated if the user first types in the value, and then sets add to true ?
This is how updateValues is called in render:
<MetricInput
value={values[index]?.change}
onChange={(e) =>
this.updateValues(e.target.value, index) //function
}
/>
Edit: There are two buttons, add and subtract. if add is set to true then I'd want to do values[index].newValue = values[index].value + values[index].change
Else, if subtract is set to true, I'd do values[index].newValue = values[index].value - values[index].change
Not withstanding there are better ways to update an array of objects, can you not consider ignoring the if(values[index].add) condition and just set values[index].add = true ?
updateValues(val, index) {
let { values } = this.state;
if (!isNaN(val)) {
values[index].change = parseFloat(val);
values[index].newValue = values[index].value + values[index].change
values[index].add = true
this.setState({
values,
});
}
}
Update - Keep the above, and consider making numbers Positive or Negative depending on Add/Subtract
const onAdd = (index) => {
let values = this.state.values;
values[index].newValue = Math.abs(values[index].newValue)
this.setState({ values })
}
const onSubtract = (index) => {
let values = this.state.values;
values[index].newValue = -Math.abs(values[index].newValue)
this.setState({ values })
}

Cannot use previous value using useRef hook. previous value is showing equal to current value

i cannot use previous value using useRef hook. previous value is equal to previous value. can someone tell me why?? and what is the solution.
i need to compare previous value of post;
const [selectedPost, setSelectedPost] = useState({ title: "", content: "" });
Following is previous value function
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
const prevVal = usePrevious(selectedPost);
Following is the function in which i use previous value
const handleClickUpdate = () => {
if (prevVal === selectedPost && isEmpty(errors))
return setEditClicked(false);
editPost(selectedPost, postId, "original", () => setEditClicked(false));
};
Edit//
i found out its happening because of this block of code. can someone explain why??
//clear validation errors on typing title or content
useEffect(() => {
if (!isEmpty(selectedPost.title) && !isEmpty(selectedPost.content))
dispatch(saveErrors({}));
}, [selectedPost, dispatch]);
It was happening because of this use Effect hook. maybe because selected post chang effect was already in use here so compiler wasnt able to execute the same effect to store previous value since this below code block came first. i removed it and instead restored errors in handle change. This solved my problem.
Previously:
useEffect(() => {
if (!isEmpty(selectedPost.title) && !isEmpty(selectedPost.content))
dispatch(saveErrors({}));
}, [selectedPost, dispatch]);
now:
const handleChange = (event) => {
const { name, value } = event.target;
// clear validation errors on typing title or content
if (!isEmpty(selectedPost.title) && !isEmpty(selectedPost.content))
dispatch(saveErrors({}));
setSelectedPost((prevVal) => {
return { ...prevVal, [name]: value };
});
};

Which approach in React is better?

Below both code does exactly same but in different way. There is an onChange event listener on an input component. In first approach I am shallow cloning the items from state then doing changes over it and once changes are done I am updating the items with clonedItems with changed property.
In second approach I didn't cloned and simply did changes on state items and then updated the state accordingly. Since directly (without setState) changing property of state doesn't call updating lifecycles in react, I feel second way is better as I am saving some overhead on cloning.
handleRateChange = (evnt: React.ChangeEvent<HTMLInputElement>) => {
const {
dataset: { type },
value,
} = evnt.target;
const { items } = this.state;
const clonedItems = Array.from(items);
clonedItems.map((ele: NetworkItem) => {
if (ele.nicType === type) {
ele.rate = Number(value);
}
});
this.setState({ items: clonedItems });
};
OR
handleRateChange = (evnt: React.ChangeEvent<HTMLInputElement>) => {
const {
dataset: { type },
value,
} = evnt.target;
const { items } = this.state;
items.map((ele: NetworkItem) => {
if (ele.nicType === type) {
ele.rate = Number(value);
}
});
this.setState({ items });
};
You can use this
this.setState(state => {
const list = state.list.map(item => item + 1);
return {
list,
};
});
if you need more info about using arrays on states, please read this: How to manage React State with Arrays
Modifying the input is generally a bad practice, however cloning in the first example is a bit of an overkill. You don't really need to clone the array to achieve immutability, how about something like that:
handleRateChange = (evnt: React.ChangeEvent<HTMLInputElement>) => {
const {
dataset: { type },
value,
} = evnt.target;
const { items } = this.state;
const processedItems = items.map((ele: NetworkItem) => {
if (ele.nicType === type) {
return {
...ele,
rate: Number(value)
};
} else {
return ele;
}
});
this.setState({ items: processedItems });
};
It can be refactored of course, I left it like this to better illustrate the idea. Which is, instead of cloning the items before mapping, or modifying its content, you can return a new object from the map's callback and assign the result to a new variable.

How to clear fields after callback from axios?

I have modal component with form. I want to inform fields of this form that form data was successfully sent to database and clear its fields.
Component code:
//ItemModal.js
addItem(e) {
e.preventDefault();
const item = {
id: this.props.itemsStore.length + 1,
image: this.fileInput.files[0] || 'http://via.placeholder.com/350x150',
tags: this.tagInput.value,
place: this.placeInput.value,
details: this.detailsInput.value
}
console.log('addded', item);
this.props.onAddItem(item);
this.fileInput.value = '';
this.tagInput.value = '';
this.placeInput.value = '';
this.detailsInput.value = '';
this.setState({
filled: {
...this.state.filled,
place: false,
tags: false
},
loadingText: 'Loading...'
});
}
...
render() {
return (
<div className="text-center" >
<div className={"text-center form-notification " + ((this.state.loadingText) ? 'form-notification__active' : '' )}>
{(this.state.loadingText) ? ((this.props.loadingState === true) ? 'Item added' : this.state.loadingText) : '' }
</div>
)
}
action.js
export function onAddItem(item) {
axios.post('http://localhost:3001/api/items/', item )
.then(res => {
dispatch({type:"ADD_ITEM", item});
dispatch({type:"ITEM_LOADED", status: true});
})
}
helper.js
else if (action.type === 'ITEM_LOADED') {
const status = action.status;
return {
...state,
isItemLoaded: status
}
}
Currently I have few issues with my code:
1. field are clearing right after click, but they should clear after changing state of loadingState. I tried to check it in separate function on in componentWillReceiveProps whether state is changed and it worked, but I faces another problem, that after closing this modal there were errors, that such fields doesn't exist.
2. loadingText should become '' (empty) after few seconds. Tried same approach with separate function and componentWillReceiveProps as at first issue.
In constructor keep a copy of your initial state in a const as follows:
const stateCopy = Object.create(this.state);
When your ajax request completes, in the sucess callback you can reset the state with this copy as follows:
this.setStae({
...stateCopy
});
One of the few ways to achieve this is to use async await which will resolve the promises and then return the value after that you can clear the values
1st approach using the async await
Here is the example
handleSubmit = async event => {
event.preventDefault();
// Promise is resolved and value is inside of the response const.
const response = await API.delete(`users/${this.state.id}`);
//dispatch your reducers
};
Now in your react component call it
PostData() {
const res = await handleSubmit();
//empty your model and values
}
Second approach is to use the timer to check the value is changed or not
for this we need one variable add this to the service
let timerFinished=false;
one function to check it is changed or not
CheckTimers = () => {
setTimeout(() => {
if (timerFinished) {
//empty your modal and clear the values
} else {
this.CheckTimers();
}
}, 200);
}
on your add item change this variable value
export function onAddItem(item) {
axios.post('http://localhost:3001/api/items/', item)
.then(res => {
timerFinished = true;
dispatch({
type: "ADD_ITEM",
item
});
dispatch({
type: "ITEM_LOADED",
status: true
});
})
}
and here is how we need to call it.
PostData = (items) => {
timerFinished = false;
onAddItem(items);
this.CheckTimers();
}
If you check this what we done is continuously checking the variable change and emptied only once its done.
One thing you need to handle is to when axios failed to post the data you need to change the variable value to something and handle it, you can do it using the different values 'error','failed','success' to the timerFinished variable.

Component's state not updating with select and onChange handler

I made a component that load data via xhr on the user select a value of <select> element.
class SomeComponent extends Component {
state = {
data: [],
currentCategory: 'all'
}
switchCategory = (ev) => {
console.log('Selected category is ' + ev.target.value);
this.setState({
currentCategory: ev.target.value
});
this.loadData();
}
loadData = async () => {
let { currentCategory } = this.state;
// Always print previous value!!!
console.log(currentCategory);
// Get data via XHR...
}
render() {
return (
<div>
<select value={currentCategory} onChange={this.switchCategory}>
<option value="all">All</option>
{categories.map( category =>
<option key={category._id} value={category.category}>{category.display}</option>
)}
</select>
<table>
// ... prints data with this.state.data
</table>
</div>
);
}
}
Above code is just in brief. Code is quite simple, I just synchronize a value of the select element with this.state.currentCategory, and detect it's switching with switchCategory method of the class.
But the main problem is that when I access the component's state, it always contains previous value, not a present. You can see that I updating the currentCategory on value of the select changes.
switchCategory = (ev) => {
console.log('Selected category is ' + ev.target.value);
this.setState({
currentCategory: ev.target.value
});
this.loadData();
}
So in this situation, this.state.currentCategory must not has "all", like something else "Apple", but still it contains "all", not an "Apple"!
loadData = async () => {
let { currentCategory } = this.state;
// Always print previous value!!! I expected "Apple", but it has "all"
console.log(currentCategory);
// Get data via XHR...
}
So eventually XHR occurs with previous value, and it gives me wrong data that I didn't expected.
After that, choosing other value of the select(let's call it Banana), it has an Apple, not a Banana!
As I know setState is "sync" job, so calling this.switchCategory will happens after updating states, so it must have present value, not a previous.
But when I print the component's state in console, it isn't.
So, what am I missing? Why am I always getting old data, not present? If I doing something wrong approach, then what alternatives can I do?
Any advice will very appreciate it. Thanks!
The problem here is that setState is async (it can be sync in certain situations). That's why you are get previous value.
There are two possible solutions.
//
// 1. use value directly.
//
switchCategory = (ev) => {
this.setState({ currentCategory: ev.target.value });
this.loadData(ev.target.value);
}
loadData = async (currentCategory) => {
console.log(currentCategory);
// Get data via XHR...
}
//
// 2. use completition callback on `setState`.
//
switchCategory = (ev) => {
this.setState({ currentCategory: ev.target.value }, () => {
this.loadData(ev.target.value);
});
}
loadData = async () => {
const { currentCategory } = this.state;
console.log(currentCategory);
// Get data via XHR...
}
Article on synchronous setState in React [link]

Categories

Resources