Reputation: 228
So I've recently started to change some functionalities into hook's
, one of which is supposed to load some components from a slides array
, get a position number from currentSlide
and then create a tag name combining these two.
import React, { useReducer, useRef } from "react";
const initialState = {
slides: ['Slide1','Slide2','Slide3'],
currentSlide: 0,
initialX: 0,
endX: 0
}
const reducer = (state, action) => {
switch (action.type) {
case 'pull-slides':
return { ...state, slides: action.value };
case 'swipe-right':
return { ...state, currentSlide: state.currentSlide + 1 }
case 'swipe-left':
return { ...state, currentSlide: state.currentSlide - 1 }
case 'set-initialX':
return {
...state, initialX: action.position
}
case 'set-endX':
return {
...state, endX: action.position
}
default:
throw new Error('Unexpected action');
}
}
const swipeDetection = () => {
const [state, dispatch] = useReducer(reducer, initialState)
const swipeArea = useRef(null)
function _onMouseDown(e) {
console.log(e.screenX)
dispatch({ type: 'set-initialX', position: e.screenX })
console.log(state)
}
function _onMouseUp(e) {
console.log(e.screenX)
dispatch({ type: 'set-endX', position: e.screenX })
console.log(state)
if ((state.initialX < state.endX) && ((state.endX - state.initialX) > 350)) {
console.log('right')
if (state.currentSlide < (state.slides.length - 1)) {
console.log('right')
dispatch({ type: 'swipe-right' })
}
} else if ((state.initialX > state.endX) && ((state.initialX - state.endX) > 350)) {
console.log('left')
if (state.currentSlide > 0) {
dispatch({ type: 'swipe-left' })
console.log('left')
}
}
}
const TagName = state.slides[state.currentSlide]
return (
typeof TagName == 'function' ? (
<div className="h-100" ref={swipeArea} onMouseDown={_onMouseDown} onMouseUp={_onMouseUp}>
<TagName />
</div>
) : ''
)
}
export default swipeDetection
In order to switch from one component to another I decided to use onMouseDown
/onMouseUp
instead of onMouseMove
events.
So when you click down it stores the screenX
and when you release the mouse is stores the screenX
again and compares the two values to see how far you've swiped left or right.
This seems to work pretty well from what I could tell, except for the fact that you have to swipe in one direction twice in order for it to actually change the currentSlide
value.
I have no idea why this is happening, granted I am really new to Hooks
and I'm just getting my code-legs (like see-legs but for programmers?).
I've tried different approaches and materials but I couldn't really find anything relevant to my case, any ideas for why something like this would happen?
I've added the console.log
for e.screenX
and state
to check and see what is happening on click, so when you click it does immediately return the e.screenX
value and the state
, but the state
is not updated with the value for some reason...
Upvotes: 2
Views: 775
Reputation: 138235
dispatch
(just like setState
and any other call to React to trigger an update) gets deferred, so it won't run immeadiately. Additionally dispatching causes a rerender, which will execute the whole component function again, which will create a new state
variable inside of it. No matter what you do, state
will always point to the old state. Therefore this:
state.initialX < state.endX
will always operate on the previous state. To solve this do the following:
1) Remove endX
and endY
from the state. If the finger gets raised you want to check for a swipe immeadiately. There is no need for persisting that position.
2) There are just two events needed for your reducer: touchdown and touchup. Everything else should be done inside the reducer.
const SlideSwiper = ({ slides }) => {
const [{ currentSlide }, touch] = useReducer(({ currentSlide, start }, { type, position }) => {
if(type === "up") {
if(Math.abs(position.x - start.x) < 350 && Math.abs(position.y - start.y) < 350)
return { currentSlide };
}
// TODO: Additional logic for swiping up & down?
return { currentSlide: currentSlide + (position.x > start.x ? 1 : -1), start: null };
} else if(type === "down") {
return { currentSlide, start: position };
}
}, { currentSlide: 0, start: null });
const touchDown = e => touch({ type: "down", position: { x: e.clientX, y: e.clientY }});
const touchUp = e => touch({ type: "up", position: { x: e.clientX, y: e.clientY }});
const slide = slides[currentSlide];
//...
});
Upvotes: 1