How to create my own onChangeComplete function for input type color on React - reactjs

With react-color https://casesandberg.github.io/react-color/ .
I can use ready-made onChangeComplete function from react-color.
But I wonder how can I create that onChangeComplete by using input type color tag.
I tried onBlur but the color won't change until user clicks or presses tab
On the other hand, using onChange keep firing updates.
Because currently I'm using redux state, so dispatching update continuously when I drag and choose color isn't a good way.
Any ideas how to create onChangeComplete?

It depends on how you'd like to define a change. To prevent continuous updates every time the mouse moves, you'll probably want to update Redux state only when the mouse stops moving. (This seems to be the behaviour on the page you linked to).
You could use the following JavaScript to detect when the mouse stops moving:
let timer;
window.addEventListener('mousemove', function (e) {
console.log('Mouse moving');
clearTimeout(timer);
timer = setTimeout(() => console.log('Mouse stopped'), 300);
});
Then, try putting the above code inside your ComponentDidMount() method and replace console.log('Mouse stopped') with whatever Redux action you want to dispatch!
300 is the number of milliseconds without movement that will trigger the action, which could be changed depending on how sensitive you want your app to feel.
Lastly, remember to remove the event listener in your ComponentWillUnmount() method.

https://github.com/casesandberg/react-color/blob/7ee60d845e5d5e11e4f6ecb895b2d9755c59d09d/src/components/common/ColorWrap.js#L30
Here is the code that how react-color implemented onChangeComplete.
It is hard coded using debounce.
I put the link there for anyone interested in using that solution

Related

How to use ReactJS's useMemo() to freeze the component's content?

The short question is: how do we freeze a component's content, probably by using useMemo() and telling it to freeze the content?
That's because useMemo(fn, []) takes the array to do a diff of values to decide whether to use the memoized value. It does not take a flag of true to tell it to use the memoized value.
I thought of one way, which is
useMemo(fn, [flag || `${Date.now()} ${Math.random()`])
so if flag is true, it won't evaluate the second part, and when it is true for a second time, the content is frozen. The second option is to use uuid() instead of the second part, which should be unique every time. The third choice is to gather all parameters that causes the output to be the same and put it into the array, which may be difficult to collect all, and is prone to bugs.
But this method is a bit hacky... and it may require comparing the performance of ${Date.now()} ${Math.random() vs uuid() because if it is CPU intensive, it only makes the situation worse.
Details:
This comes from wanting to slide a panel out and not update it, because the panel is very busy updating and the main window is busy updating continuously. To do that, when the user click the "Update Main Window" button, we dispatch an action to set the redux state, and we can slide out the panel, and on complete, we dispatch another action so that a redux state will tell the panel not to update and just return <div></div>. Another way is to just dispatch the first action, and be able to "freeze" the component. In this case, we don't need to dispatch the second action.
But useMemo() doesn't take a "freeze" flag, and take an array of dependencies instead. Is there a way to use a flag to cause it to freeze?
If I understand correctly your actual dependency is on the "Update Main Window" button.
You can add state like this and it should update content after user clicks on the button.
useMemo documentation
const [updateFlag, setUpdateFlag] = useState(false);
useMemo(() => {}, [updateFlag]);
return (
<div>
<button onChange={() => setUpdateFlag(prevUpdateFlag => !prevUpdateFlag)}>update main window</button>);

Trying to trigger a WheelEvent programmatically with Hammerjs

I am using a library (https://github.com/asmyshlyaev177/react-horizontal-scrolling-menu) that scrolls on use of the mousewheel, and I want to use this functionality when swiping left or right.
I am using hammerjs to replicate swipeleft and swiperight behavior, and this is working.
However, creating a WheelEvent does not seem to trigger the functionality dependent on the WheelEvent.
I am using componentDidUpdate for now as my react lifecycle method because for some reason this.containerRef.current is always null in componentDidMount, but once I figure out the reason behind that, I'll probably move it.
Anyway, here's my code:
componentDidUpdate() {
if(this.containerRef.current !== null) {
this.hammer = Hammer(this.containerRef.current)
this.hammer.on('swiperight', () => alert("swipe right"));
var wheelevent = new WheelEvent("wheel", {deltaX: 500, deltaY: 500});
this.hammer.on('swiperight', () => window.dispatchEvent(wheelevent));
}
}
Now I want to point out, the alert for swipe right DOES happen, so the behavior is definitely triggering, however my WheelEvent is not being caught by the scroll library.
How should I trigger a WheelEvent programmatically?
EDIT - I made a codepen about it:
https://codesandbox.io/s/react-horizontal-scrolling-menu-fi7tv
My hunch is that issue is related to Dragging being disabled and the event is canceled.
So you need to send the event down the chain a bit. I have updated the codesandbox below which works
https://codesandbox.io/s/react-horizontal-scrolling-menu-j46l8
The updated code part is below
var elem = document.getElementsByClassName("menu-wrapper")[0];
this.hammer.on("swiperight", () => elem.dispatchEvent(wheeleventRight));
this.hammer.on("swipeleft", () => elem.dispatchEvent(wheeleventLeft));
You may want to better the approach though in a more reactive fashion later. But this does show that once you sent the event in lower order elements the wheeling does work well

Force React to rerender quickly on some visually important state changes

I need to propagate state changes to user screen as quickly as possible for some important UI elements, defer other element renderring a bit.
I know about setState's callback, it doesn't help me here.
I think fiber priorities could help me, but I don't know how to use them.
Example:
I have a button that must be disabled immediately after click.
I also have many other slow components that change on that button click.
React batches rendering of the disabled button and other slow components together so the button does not get disabled immediately.
Current workaround is to delay other state changes, to make React immediately disable the button, and only then start to modify other components:
this.setState({ enabled: false }, () => {
this.debounce = setTimeout(() => {
this.props.onModified(value);
}, 200);
})
Is there some explicit way to tell React to be fast to render in some important state changes, without batching them?
(The problem is not only with buttons, but with immediate closing of the modal dialogs as well)
https://codesandbox.io/s/kk4o612ywr
You can use callback function of the setstate, something like this, which will ensures the rendering of the first change. so, your button will get the disabled first and then you can update your state with different operations. using timeout will not be accurate as there is fixed timing which will cause the inaccurate results.
Here is what I did:
this.setState({ enabled1: false },function(){
this.setState(prevState => ({ data: prevState.data + 1 }));
});
Demo

React onTransitionEnd event gets called frequently

I have React code like this:
handleTransitionEnd() {
console.log('ended');
}
<div onTransitionEnd={(ev) => this.handleTransitionEnd(ev)}....
The peculiar thing is that my logs are filling up with ended. Even if I don't have any transition css logic tied to the element at all. It seems to be firing on every state change or re render. Is this normal? I would expect it to only fire when a css transition ends.
Is there some other way this callback should be achieved?
Thanks
UPDATE:
here is a sandbox showing some strangeness: https://codesandbox.io/s/vy5wwyq5v3
Clicking the first button causes the callback to be called 3 times, then if you click the second button it gets called another 2 times even tho a transition doesn't happen. My app is even more extreme than this with it getting called a lot more often.
The css transition being used transition: "all 3s" is causing transitions on multiple properties, not just margin-left.
In the example provided, outline-color and outline-width were transitioned as well. They were then transitioned again when clicking anywhere else (not just on the second button).
You can see this by checking the event:
onTransitionEnd={e => {
e.persist(); // see: https://reactjs.org/docs/events.html#event-pooling
console.log(e.propertyName);
}}
The css transition could be more specific: transition: "margin-left 3s" to avoid this.
Alternatively, test e.propertyName for the desired case.
To add on to what dabs said in his answer, the transitionend event bubbles. This means if any children of your element have transitions on them, they will trigger the transitionend event on themselves, then bubble up to their parent, triggering on each of them, including the current element that you have the listener on.
A simple way to check that you're only running your logic for the element your listener is on is to compare e.target and e.currentTarget like so:
onTransitionEnd={e => {
if ( e.target === e.currentTarget ) {
// your logic here
}}
e.target is the element that starts the event, which can be one of the children, for example, not just the element with the listener on it.
e.currentTarget however always points to the element that has the listener on it, regardless of if it initiated the event or not.
By comparing the two, you can check that you're only running logic when it's the element with the listener on it that initiated it.

Store update doesnot reflect

I am following React & Redux. I have actions, reducers working separately. I am managing state more or less properly.
I am trying to create a ui which is having few textboxes and a button(disabled). In text boxes i had added onchange event on which it call a function
onChangeTextbox(){
}
In this function I do the following thing.
1. I update the state of the redux store.
onChangeTextbox(){
updateStateOfTextBox();
}
2.After doing this I look for whether all the text boxes, in the ui, is having something in it. If so I will enable my button to do further operations.
onChangeTextbox(){
updateStateOfTextBox();
updateStateOfButton();
}
Everything is working good except the one thing.
That one thing is as soon as I give the last empty textbox one character, the button is not enabled immediately, and when I give more character the button gets enabled. Similarly vice versa for disabling button.
The problem which I found is that when control complete its job of function updateStateOfTextBox(); and enters the updateStateOfButton(); function the state remains same. And again when render() occurs the change in state is reflected then.
I want to fix that issue and I am not getting any way out. Any solution and suggestion to this will be appreciated.
Thanks.
I found the solution to such situation.
componentsWillRecieveProps(newProps){
}
this method of the life cycle changes the game.
componentsWillRecieveProps(newProps){
newProps.state // this will give you new updated state which your action updated.
this.props.state // this will give you local or your state
// Here you can update your local state with global state
// And call the other function here as
updateStateOfButton();
}
Therefore the solution can be interpreted like
onChangeTextbox(){
updateStateOfTextBox();
}
componentsWillRecieveProps(newProps){
.
.
updateStateOfButton();
}

Resources