I have a motion value that is updated whenever a component is being hovered. I thought that framer-motion automatically took care of animating the value to its new state but apparently it doesn't. How can I transition the new values of my motion value? It is also important to note that I'm aware of the whileHover prop that exists but I don't want to use it here.
This example component illustrates my situation:
const Component = () => {
const scale = useMotionValue(defaultScale);
return (
<motion.img
style={{ scale }}
onMouseEnter={ () => scale.set(1.35) }
onMouseLeave={ () => scale.set(defaultScale) }
/>
)
}
Have you tried useSpring?
const Component = () => {
const scale = useSpring(1);
return (
<motion.div
style={{ scale, background: "tomato", width: "100px", height: "100px" }}
onMouseEnter={() => scale.set(1.35)}
onMouseLeave={() => scale.set(1)}
/>
);
};
Docs: https://www.framer.com/api/motion/motionvalue/#usespring
Or you can also use useAnimation to create custom controls and transitions:
const Component2 = () => {
const controls = useAnimation();
return (
<motion.div
style={{ background: "blue", width: "100px", height: "100px" }}
animate={controls}
onMouseEnter={() => controls.start({ scale: 1.35 })}
onMouseLeave={() => controls.start({ scale: 1 })}
/>
);
};
Docs: https://www.framer.com/api/motion/animation/#animation-controls
Codesandbox with both examples: https://codesandbox.io/s/httpsstackoverflowcomquestions64077992-j63qv?file=/src/App.js
Related
When I am using scroll down so that time our function "fetchMoreData"?
I want to scroll down that time my function is called
const fetchMoreData = () =>{
console.log("pulkit")
}
<InfiniteScroll
dataLength={25}
next={fetchMoreData}
hasMore={true}
loader={<h4>Loading...</h4>}>
<Grid container spacing={4} sx={{ marginTop: '1rem' }}>
{simulationList.map((item, i) => (
<Grid
item
xs={3}
key={item?._id}
>
</Grid>
))}
</Grid>
</InfiniteScroll>
Try using the different scroll libraries of functions to scroll in react.
for my code, i have been using this "react-scroll-wheel-handler" which allows me to display only the required number of items on the screen and even allows me to change the setting in the UI where i can change the number of results in the output table.
https://www.npmjs.com/package/react-scroll-wheel-handler
I've tried to recreate your scenario.It's from react-infinite-scroll-component
example.. Try this out
Sandbox code
you don't need any lib you can do using it to react onScroll event pls find the working demo here
import React from "react";
import { render } from "react-dom";
const style = {
height: 30,
border: "1px solid green",
margin: 6,
padding: 8
};
class App extends React.Component {
state = {
items: Array.from({ length: 20 })
};
fetchMoreData = () => {
console.log("pulkit");
setTimeout(() => {
this.setState({
items: this.state.items.concat(Array.from({ length: 20 }))
});
}, 1500);
};
onHandleScroll = (e) => {
e.persist();
if (e.target.scrollHeight - e.target.scrollTop === e.target.clientHeight) {
this.fetchMoreData()
}
}
render() {
return (
<div onScroll={this.onHandleScroll} style={{
overflow: "scroll", height: '800px'
}}>
<h1>demo: react-infinite-scroll-component</h1>
<hr />
{this.state.items.map((i, index) => (
<div style={style} key={index}>
div - #{index}
</div>
))}
</div>
);
}
}
render(<App />, document.getElementById("root"));
I'm trying to add support for touch events on slider inputs in my React app. Taps and drags work fine with bare React. The only other touch event I need is onTouchEnd to determine when a user has finished dragging a slider and the new value is to be committed.
I'm trying to use react-swipeable. I don't know if my question is specific to Swipeable or more generally to React. I've created the suggested handler for the onTouchEnd event and it works perfectly, but for only one element. I am mapping through 20 or 30 sliders and find that only the LAST slider works properly; the other sliders do not fire the handler at all.
I've tried it with and without the refPassthrough enhancement. The problem may be my limited understanding of useRef.
In the code below, three horizontal divs are created. Touching on the last (blue) div logs the event; the others don't.
I've also provided this working code in CodeSandBox
Any help would be appreciated,
Bill
import { useRef } from "react";
import { useSwipeable } from "react-swipeable";
export default function App() {
const handlers = useSwipeable({
onTouchEndOrOnMouseUp: (e) => console.log("User Touched!", e)
});
const myRef = useRef();
const refPassthrough = (el) => {
handlers.ref(el);
myRef.current = el;
};
return (
<div>
<h1>Re-use Swipeable handlers </h1>
<div
{...handlers}
style={{ backgroundColor: "red", height: "50px" }}
></div>
<div
{...handlers}
ref={refPassthrough}
style={{ backgroundColor: "green", height: "50px" }}
></div>
<div
{...handlers}
style={{ backgroundColor: "blue", height: "50px" }}
></div>
</div>
);
}
This is AN answer. It doesn't work perfectly for me because my use is within a function, not a functional component. I will try to re-work my app so that I can call useSwipeable only within functional Components.
import { useSwipeable } from "react-swipeable";
export default function App() {
return (
<div>
<h1>Re-use Swipeable handlers </h1>
<div
{...useSwipeable({
onTouchEndOrOnMouseUp: () => console.log("touch red")
})}
style={{ backgroundColor: "red", height: "50px" }}
></div>
<div
{...useSwipeable({
onTouchEndOrOnMouseUp: () => console.log("touch green")
})}
style={{ backgroundColor: "green", height: "50px" }}
></div>
<div
{...useSwipeable({
onTouchEndOrOnMouseUp: () => console.log("touch blue")
})}
style={{ backgroundColor: "blue", height: "50px" }}
></div>
</div>
);
}
Here is a more complete answer. Also in CodeSandbox
// It's surprisingly hard to process an onTouchEnd event for a slider in React.
// The useSwipeable hook does the heavy lifting.
// However, because it is a hook, it can't be .map-ped unless it is
// wrapped in a component.
// And, once it is wrapped in a component, it is hard to communicate
// onChange events to a parent component (the ususal tricks of passing
// setState or other changehandler do not seem to work for continuous
// slider onChange events.
// The approach here is to handle all of the onChange stuff in the wrapped
// component, including a local feedback display.
// Then, on the onTouchEnd event, the "normal" communication of the final
// value is returned to the parent via the dispatch prop.
import { useReducer, useState } from "react";
import { useSwipeable } from "react-swipeable";
export default function App() {
const [reduceState, dispatch] = useReducer(reducer, {
name1: "33",
name2: "66"
});
function reducer(state, action) {
return { ...state, [action.type]: action.data };
}
const MapWrappedSlider = (props) => {
const [currentValue, setCurrentValue] = useState(props.initialValue);
return (
<div style={{ backgroundColor: "cornsilk" }}>
<h2>{currentValue}</h2>
<input
type="range"
value={currentValue}
{...{
onChange: (e) => setCurrentValue(e.target.value),
onMouseUp: () =>
dispatch({ type: props.paramName, data: currentValue })
}}
{...useSwipeable({
// note: the current version of useSwipeable does not actually
// handle onMouseUp here. Also, the advertised onTouchEnd
// does not actually handle onTouchEnd
onTouchEndOrOnMouseUp: () =>
dispatch({ type: props.paramName, data: currentValue })
})}
/>
</div>
);
};
return (
<div style={{ textAlign: "center" }}>
<h1>SWIPEABLE MAPPED SLIDERS</h1>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-around"
}}
>
<h2>{reduceState.valueName}</h2>
{["name1", "name2"].map((paramName) => {
return (
<div key={paramName}>
<h1>{reduceState[paramName]}</h1>
<MapWrappedSlider
paramName={paramName}
initialValue={reduceState[paramName]}
dispatch={dispatch}
/>
</div>
);
})}
</div>
</div>
);
}
I'm trying to make a map that will show the name of the country when you hover your cursor. For this I am using react-simple-map with react-tooltip:
function OnHoverMap() {
const [content, setContent] = useState("");
return (
<div>
<Map setTooltipContent={setContent} />
<ReactTooltip>{content}</ReactTooltip>
</div>
)
}
const Map = ({ setTooltipContent }) => {
return (
<>
<ComposableMap data-tip="" projectionConfig={{ scale: 200 }}>
<ZoomableGroup>
<Geographies geography={geoUrl}>
{({ geographies }) =>
geographies.map(geo => (
<Geography
key={geo.rsmKey}
geography={geo}
onMouseEnter={() => {
const { NAME } = geo.properties;
setTooltipContent(`${NAME}`);
}}
onMouseLeave={() => {
setTooltipContent("");
}}
style={{
default: {
fill: "#D6D6DA",
outline: "none"
},
hover: {
fill: "#F53",
outline: "none"
},
pressed: {
fill: "#E42",
outline: "none"
}
}}
/>
))
}
</Geographies>
</ZoomableGroup>
</ComposableMap>
</>
);
};
This works fine, but there is a problem when zooming and moving the map - the focus shifts to the overall map (ComposableMap), and the panel with country name appear above the map, not over the country:
On react-simple-maps website mentions, what is not possible to set data-tip on Geography directly.
Is there a way to solve this problem? Does it help if I make ComposableMap non-focusable?
Instead of adding "data-tip" to ComposableMap, add it to the outer div which is holding your Map and ReactTooltip.
I would like to know which are the benefits of using React Lazy and Supense against what I am showing you below. First Example is using useState and Onload Prop, and second example is using React Lazy and Suspense.
const ImageComponent = ({ src }) => {
const [load, setLoad] = useState(false);
//handles load of the image
const handleOnload = () => {
setLoad(true);
};
return (
<>
{!load && <Spinner>}
<img
src={src}
alt="Avatar"
onLoad={handleOnload}
style={{
height: '192px',
width: '50%',
display: load ? 'inline' : 'none',
}}
/>
</>
);
};
VS this
<Suspense
fallback={
<Spinner style={{ width: '50%' }} />
}
>
<ImageComponent src={listItem.download_url} />
</Suspense>
ImageComponent.js
const ImageComponent = ({ src }) => {
return (
<img
src={src}
alt="Avatar"
style={{
height: '192px',
width: '50%',
}}
/>
);
};
export default ImageComponent;
JSFiddle
Code:
export default function App() {
const spring = useSpring({ from: { opacity: 0 }, to: { opacity: 1 } });
const [ref] = useInView();
rerenders++;
return (
<div style={{ height: "200vh" }}>
<div style={{ height: "150vh" }}></div>
<animated.div
ref={ref}
style={{
height: "50px",
width: "50px",
backgroundColor: "red",
opacity: spring.opacity
}}
>
Hello!
</animated.div>
</div>
);
}
Attaching useInView's ref (a hook from react-intersection-observer) causes constant rerendering of the component. Why is that so?
Using an IntersectionObserver yourself does not do such a thing:
const ref = useRef<any>();
useLayoutEffect(() => {
const obs = new IntersectionObserver((entries, observer) => {
entries.forEach((entry, index) => {
console.log(entry);
});
});
obs.observe(ref.current);
}, []);