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).
Related
Forgive me there are a lot of questions asking this same thing but from over 10+ years ago.
Is there any way to checkmark a group of checkboxes based on an array in React? I have an array saved within state (stepThree) that I need to pulldown when a user returns to this screen within a multistep form. I'm looking for a way that the values within that array become/stay checked upon return to that screen so it shows the user their previous selections.
Current set-up explained below
State is opened with empty checkedBox array and stepThree initialized to pull responses later. checkedBox is eventually cloned into stepThree.
this.state = {
checkedBox: [],
stepThree: this.props.getStore().stepThree,
};
Boxes that are checked by the user are added to checkedBox array or removed if unchecked.
handleCheckboxChange = (event) =>{
const isChecked = event.target.value; //Grab the value of the clicked checkbox
if (this.state.checkedBox.includes(isChecked)) {
// If the checked value already exists in the array remove it
} else {
// If it does not exist, add it
}
}
Validate and store the completed array on clicking next
if (Object.keys(validateNewInput).every((k) => { return validateNewInput[k] === true })) {
if (this.props.getStore().stepThreeObjects != this.state.checkedBox) { // only update store of something changed
this.props.updateStore({
// Store the values of checkedBox inside stepThree and run updateStore to save the responses
});
} else {
// Return an error
}
Sample checkbox
<label className="choice-contain">
<span>Checkbox Sample</span>
<input
value="Checkbox Sample"
name="Question 3"
type="checkbox"
onChange={this.handleCheckboxChange}
/>
</label>
I've tried to create a persistCheckmark function that pulls the values of the array from stepThree and then does a comparison returning true/false like I do in the handler but since this is not an event I can't figure out how to trigger that function on return to the step.
Currently when returning to the step nothing is checked again and I believe that has to do with checkedBox being initiated as empty.
persistCheckmark(event) {
const isChecked = event.target.value; //Grab the value of the clicked checkbox
if (this.state.stepThree.includes(isChecked)) {
return true;
} else {
return false
}
}
Figured it out thanks to an old post here: How do I set a group of checkboxes using values?
Just updated the filter for when the component mounts
componentDidMount() {
if (this.state.stepThree != undefined) {
var isChecked = this.state.stepThree
$('input[type="checkbox"]').filter(function() {
return $.inArray(this.value, isChecked) != -1;
}).prop('checked', true);
} else { return }
}
and then added a ternary in the state initiation to check the storage and copy it over so it doesn't initialize as empty every time.
checkedBox: this.props.getStore().stepThree != undefined ? this.props.getStore().stepThree : [],
I'm trying to insert an empty array into localStorage if item doesn't exist:
static getLocalActivities(){
var localActivities = localStorage.getItem("Local-Activities");
if (localActivities === undefined){
LocalStorageFunctions.createLocalActivities()
return JSON.parse(localStorage.getItem("Local-Activities"))
}
else{
if (localActivities.length === 0){return localActivities}
else{
// return JSON.parse(localActivities) // fails because not empty arr
return localActivities;
}
}
}
static createLocalActivities(){
return localStorage.setItem('Local-Activities', []); // < this doesnt work
// return localStorage.setItem('Local-Activities', JSON.stringify([])); // < this doesnt work
}
both methods for creating the item always in application chrome tools show:
Problem
This var localActivities = localStorage.getItem("Local-Activities"); returns 'undefined' and not undefined - take notice that the first one is a string.
This if is always false if (localActivities === undefined){ because localActivities is 'undefined' not undefined
Hence createLocalActivities never runs
Solution
Clear the localStorage value from the dev tools
Use JSON.stringify to save values as suggested in the comments
So I have two modals that I am using one of them was already implemented and behaves as expected however when I've added the other modal depending on the condition of if there is any true value when mapping over the array the way it works right now both modals show when there is a true value. I think this is because there are multiple false values returned from my .includes() function before the true appears. I think a good solution for this would be to make an array of all the values returned when I run .includes() on the entries then I can check that array for any true values but I cant seem to get the values into an array. When I try and push them into an array they just all push into their own separate arrays. This may be the wrong approach if it is can you explain what a better approach would be:
const checkPending = () => {
if(entries){
entries.map(descriptions => {
const desc = descriptions.description
//check if there are any pending tests
const check = desc.includes("pending")
//if the check returns true show the pending modal if it doesnt set the other modal to true
if(check === true){
setShowModal(false)
setShowPendingM(true)
}else{
setShowModal(true)
}
})
}
}
return(
<Button
onClick={() => checkPending()}
className={`${styles.headerButton} mr-2`}
>
Add File
<Plus />
</Button>
)
setShowModal & setShowPendingM are both passed from a parent component as props. They are both initialized as false. The most straightforward question I can pose is is there any way to say if there are any true values returned from .includes then do something even if there are false values present
I think this is how your checkingPending method should look like.
const checkPending = () => {
if(entries){
let pending = false;
entries.forEach((descriptions) => {
const desc = descriptions.description
if(desc.includes('pending')){
pending = true;
}
});
if(pending) {
setShowModal(false);
setShowPendingM(true);
} else {
setShowModal(true);
setShowPendingM(false);
}
}
}
Let me know if you have any additional questions.
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.
I'm trying to make a change on a page, once addListItem is ran an array called "list" that is actually a redux state, needs to be updated. I managed to update it, but instead of an array I return an object. I need to return an array instead, but I don't know how to refactor the code to make it do that.
/**
* Add Item
*/
case 'playlist/addListItem_success': {
return {
...state,
list: {
...state.list,
[action.meta.position]: {
...state.list[action.meta.position],
status: true
}
}
}
}
To return an array, you'd have to use the array-spread syntax (e.g. [...someArray]) instead of object spread, but you can't use that to update a particular index. With a map you can elegantly express what you need though:
return {
...state,
list: state.list.map((e, i) => i === action.meta.position ? {...e, status: true} : e)
};