React Beginner Question: Confusing Arrow Function Example - javascript

I'm a React/ES6 beginner and I'm using this code I found to handle a checkbox inside a custom component being clicked (the custom component includes a material-ui CheckBox, hence the "checked" value). I'm planning on adding more fields to the custom component, such as a textbox that corresponds to the checkbox where the user can add more information about the box they checked.
Anyway, I'm a bit confused about what's going on in that first line. I was hoping one of you senior level devs could break it down for me so i can understand what's happening here.
Two things to note:
index console logs as an integer value (position in my mapped array)
checked is false by default but console logs as true (is it being
toggled true somehow?)
const onMediaDeliverableChange = index => ({ target: {checked} }) => {
console.log('>> [form.js] (onMediaDeliverablesChange) index = ',index);
console.log('>> [form.js] (onMediaDeliverablesChange) target = ',checked); }
Here's an example of code that I took this from, that is working.
const onCheckBoxChange = index => ({ target: { checked } }) => {
const newValues = [...values];
const value = values[index];
newValues[index] = { ...value, checked };
console.log('>> [form.js] (onCheckBoxChange) value = ',value, index);
console.log('>> [form.js] (onCheckBoxChange) newValues[index] = ',newValues[index]);
props.setDesignOrDigital(newValues);
};

The following:
const onCheckBoxChange = index => ({ target: { checked } }) => {
// stuff
};
could also be written as:
const onCheckBoxChange = index => (event) => {
const checked = event.target.checked;
// stuff
};
{ target: { checked } } is an example of using object destructuring on a function argument. In this case, the argument would be an event.
The first part:
index => another-function
is, as svey mentioned in the comments, an example of a curried function. I assume onCheckBoxChange was used in a loop where the index of the checkbox being rendered was passed in which then returns a function that is an event handler for the change event. The index is then used to get/set the checked state for the respective checkboxes.

Related

Why my delete function is not returning on page array after I delete element from it

Below is the delete function which is working, it takes an argument and deletes the element with the index in the argument.
Setitem is a useState hook and is defined like this
when we click the on add list is added to item array
const listofitem = () => {
setitems((e) => {
return [...e, list];
});
};
This function is working.
const deleteItems = (e) => {
setitems((e1)=> {
return e1.filter( (er, index)=> {
return index!=e;
});
});
}
Why is this not working the splice method is working fine.
const deleteItems = (e) => {
items.splice(e-1, 1);
setitems((e1)=>{
return e1;
})
};
here is the code sandbox for the same, the function is working in this. But I want to know why the other is not working.
https://codesandbox.io/s/old-wood-cbnq86?file=/src/App.js:0-1169
This is how I got it to work, in your example you try to mutate the state directly and then not really set the state. If you want to use the state on setState you need to mutate what you call e1.
However you have some issues with your codesandbox that will trigger the function more than once sometimes
setitems((e1) => {
e1.splice(e, 1);
return [...e1];
});
Edit author updated question, this is not longer relevant.
In the first deleteItems function you never use e which I assume is the index you want to delete.
const deleteItems = (deleteIndex) => {
setitems((list)=> list.filter((_, index)=> index !== deleteIndex));
};

Search bar in react works, but experiencing very strange behaviour

In a component when an input is provided it dispatches the input to a reducer
const changeSearchTerm = (e) => {
setSearchTerm(e.target.value)
dispatch(userActions.searchByName(searchTerm));
}
Then the reducer takes that value and tries to filter the array based on that input
searchByName: (state, action) => {
state.search = action.payload;
let newArray = state.users.filter((user) =>
user.name.toLowerCase().includes(state.search.toLowerCase())
);
state.users = newArray;
},
Then this whole array is displayed in one of the other components. The search bar works to some extent:
When you start typing into the search bar it only starts to work after second input. In console I have noticed that if you type "Timmothy" it will only register "Timmoth" in the state.
Additionaly, if I delete text from the input, the state of the array doesn't update and stays the same even though the input value changes.
So what happens currently is if you start typing in the search bar, it updates the array, but always only registers second to last keystroke. Additionally, if you delete input from the search bar, it doesn't register. The value changes, but the array stays with only few items left.
I have tried to not use the additional variable to store the state and do it like this
state.users.filter((user) =>
user.name.toLowerCase().includes(state.search.toLowerCase())
But then absolutely nothing happens. Would appreciate if someone could tell me what I'm missing here
EDIT
This is the component that receives that input
<input onChange={changeSearchTerm} type="text" value={searchTerm}></input>
And the searchTerm value is saved in useState - const [searchTerm, setSearchTerm] = useState("");
From what I can tell, it only works after the second input because of this block:
const changeSearchTerm = (e) => {
setSearchTerm(e.target.value)
dispatch(userActions.searchByName(searchTerm));
}
State setters like setSearchTerm are actually asynchronous. This means that after you call setSearchTerm, on the next line the value of searchTerm is still the old value.
You can instead do one of the following:
const changeSearchTerm = (e) => {
setSearchTerm(e.target.value)
dispatch(userActions.searchByName(e.target.value));
}
const changeSearchTerm = (e) => {
setSearchTerm(e.target.value, () => {
// This callback runs only after state has changed
dispatch(userActions.searchByName(searchTerm));
});
}
Edit: Sorry I missed that you used useState/functional. You can use useEffect:
const changeSearchTerm = (e) => {
setSearchTerm(e.target.value);
}
useEffect(() => {
if (searchTerm.length > 0) {
dispatch(userActions.searchByName(searchTerm));
}
}, [searchTerm]);
This way your dispatch will correctly run whenever searchTerm's value changes for sure.
https://reactjs.org/docs/react-component.html#setstate

handling state during onChange event

handling state during onChange event, aray of mutiple objects , and objects with multiple key Value pair, dunring onChange how to handling and update the state with out overwrite any object or value of objects
// state exits with array of multiple objects and objects with multiple key value pairs.
const [employement, setEmployement] = useState([{title:"react_js", exp:"good", id:1}, {title:"laravel", exp:"master", id:2}, {title:"node_js", exp:"well", id:3},{title:"flutter", exp:"mid", id:4}])
// method to change the input value
const handleChange =(id, inputValue)=>{
// need help how to change the single value , without any over in object value and without overRIght whole object in an array .
employement.filter((emp) => {
if (emp.id === id) {
setEmployement([{ ...emp, job_title: inputValue }]);
}
});
// but upper logic not working correctly, this code need improvement
}
// map method to iterate the array
employement.map(singleEmp=>{
<input type="text" value={singleEmp.title} onChange={(e)=>handleChange(singleEmp.id, e.target.value)}/>
})
// last this is dummy structure of my real code, real code is long enough but same as it, thank you for your any suggestions
Try this as your handleChange method
// method to change the input value
const handleChange = (id, inputValue) => {
setEmployement((prevstate) => {
return employement.map((emp) => {
if (emp.id === id) {
return { ...emp, title: inputValue };
}
return emp;
});
});
};
Code sandbox => https://codesandbox.io/s/nice-greider-0efof?file=/src/App.js

Set state if any of the value in array exists

I have a listener that sets the state if the value exists. It previously worked fine when I just had one value like so.
subscribe('feature-settings-updated', (evt, enabledFeatures) => {
setIsEnabled(enabledFeatures.includes('showEmployeesReport'));
});
Now I have a second value and rather than duplicating the code I want to do it so that it takes an array and if either one of the value exists then set it to true. Following is my attempt but it only enables it for just the one. Any ideas?
const features = ['showEmployeesReport', 'showCustomersReport'];
subscribe('feature-settings-updated', (evt, enabledFeatures) => {
setIsEnabled(
features.some(exp => enabledFeatures.includes(exp))
);
});
You seem to reference the wrong variable (experiments instead of features):
const features = ['showEmployeesReport', 'showCustomersReport'];
subscribe('feature-settings-updated', (evt, enabledFeatures) => {
setIsEnabled(
features.some(feature => enabledFeatures.includes(feature))
);
});

Callback in React has original props from first render, not the render when the callback is called

Edit: The solution is to redefine the callback on each render by redefining the timeline.on outside the effect, but I don't understand why this is necessary still.
Edit 2: I've realized I need to re-add and remove all listeners for each render. Here is the working code. I still don't understand the scoping concept on why the original question doesn't work.
useEffect(() => {
const onSelect = (properties: { items: IdType[]; event: MouseEvent }) => {
if (!(properties.items.length > 0)) return;
const selectedItem = items.find(item => item.id === properties.items[0]);
if (selectedItem) onItemSelect(selectedItem);
};
const addListeners = (listenerTimeline: VisTimeline) => {
listenerTimeline.on('select', onSelect);
};
const removeListeners = (listenerTimeline: VisTimeline) => {
return () => {
listenerTimeline.off('select', onSelect);
};
};
if (timeline) {
addListeners(timeline);
return removeListeners(timeline);
}
if (!timeline) {
const newTimeline = new VisTimeline(container.current, items, groups, options);
addListeners(newTimeline);
setTimeline(newTimeline);
return removeListeners(newTimeline);
}
}, [timeline, items, groups, time.start, time.end, onItemSelect]);
Original Post:
The code in question is here:
export const Timeline: FunctionComponent<TimelineProps> = ({
items,
groups,
time,
onItemSelect,
}) => {
const [timeline, setTimeline] = useState<VisTimeline | undefined>(undefined);
useEffect(() => {
const options: TimelineOptions = {};
if (!timeline) { // This succeeds only on the first render, and at this time the items prop is an empty array.
const newTimeline = new VisTimeline(container.current, items, groups, options);
newTimeline.on('select', (properties: { items: IdType[]; event: MouseEvent }) => {
console.log(`items: ${items}`); // This logs an empty array, but there is an array of data in this prop when the callback is called
if (!(properties.items.length > 0)) return; // This is good, has an id of an object
const selectedItem = items.find(item => item.id === properties.items[0]);
if (selectedItem) onItemSelect(selectedItem);
});
setTimeline(newTimeline);
}
}, [timeline, items, groups, time.start, time.end, onItemSelect]);
The first time this is rendered, the useEffect is called and gets past the first if statement. It will only run one time because for each subsequent render timeline is already defined and this effect exits.
The items prop is an empty array when the effect is run. However, when the callback is called (much later) the items prop is an array with data in it to search. I have verified this in the React Dev Tools.
What newTimeline.on does is register a callback that occurs when an item in my timeline is selected.
So while the items prop has an array with data, for whatever reason the items variable in the scope of the callback is still the original empty array from the first render this ran.
It's an arrow function, but does the callback capture it's environment when it is called the first time and not re-evaluate later? Thanks for any help.

Categories