How to pass mouse events between overlapping components in ReactJS - javascript

I'm building a page with two overlapping components and i want to pass mouse events from the top component to the bottom one.
I'm using ReactJs and the code looks like this:
export default function App() {
return (
<>
<SkyBox className='Streetview' onClick={()=>console.log("you clicked STREETVIEW!")}/>
<Canvas className='Canvas' onClick={()=> console.log("you clicked THREEJS")}>
<Camera position={[0, 0, -20]} />
<ambientLight intensity={0.7} />
<spotLight position={[10, 10, 10]} angle={0.90} penumbra={1} />
<pointLight position={[-10, -10, -10]} />
<Suspense fallback={null}>
<Scene />
</Suspense>
</Canvas>
</>
);
}
so I want to get the mouse events from the Canvas component and pass them to the streetview SkyBox one so the user could navigate the streetview panorama and therefore the events i'm interested in is the mouseClick , mouseMove and mouseOut.

You can pass events as props like so:
import { useState } from 'react'
const Wrapper = () => {
const [event, setEvent] = useState(null)
return (
<div onClick={(e) => setEvent(e)}>
Lorem Ipsum
<Component event={event} />
</div>
)
}
const Component = (event) => {
console.log(event) // → event: SyntheticBaseEvent
return null
}
export default Wrapper

I'm not sure if you need the current state of the mouse-events but doesn't matter if state or the setters you can use context for this.
export const CanvasContext = createContext();
export default function App() {
const [mouseClicked, setMouseClicked] = useState(...);
const [mouseMoveState, setMouseMoveState] = useState(...);
const [mouseOut, setMouseOut] = useState(...);
return (
<CanvasContext.Provider value={{
mouseClicked,
setMouseClicked,
mouseMoveState,
setMouseMoveState,
mouseOut,
setMouseOut
}}>
<SkyBox
className="Streetview"
onClick={() => console.log("you clicked STREETVIEW!")}
/>
<Canvas
className="Canvas"
onClick={() => console.log("you clicked THREEJS")}
>
<Camera position={[0, 0, -20]} />
<ambientLight intensity={0.7} />
<spotLight position={[10, 10, 10]} angle={0.9} penumbra={1} />
<pointLight position={[-10, -10, -10]} />
<Suspense fallback={null}>
<Scene />
</Suspense>
</Canvas>
</CanvasContext.Provider>
);
}
then use inside Canvas/SkyBox/... the state/setter you need like this.
export default function Canvas() {
const {setMouseClicked, setMouseMoveState} = useContext(CanvasContext);
return (
<div>
.....
</div>
);
}

Related

Context not working properly in React-Native

Here is my Context File:
import React from 'react';
import { createContext, useState } from 'react';
export const TabContext = createContext({
opened: false,
toggleOpened: () => {},
});
export const TabContextProvider = ({ children }) => {
const [opened, setOpened] = useState(false);
const toggleOpened = () => {
setOpened(!opened);
};
return (
<TabContext.Provider value={{ opened, toggleOpened }}>
{children}
</TabContext.Provider>
);
};
My Simplified App.js File: (Necessary files are imported)
const Tab = createBottomTabNavigator();
export default function App() {
const buttonCtx = useContext(TabContext);
return (
<>
<TabContextProvider>
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen
name='Action'
component={ActionScreen}
options={{
tabBarButton: () => (
<ActionButton
opened={buttonCtx.opened}
toggleOpened={buttonCtx.toggleOpened}
/>
),
}}
/>
</Tab.Navigator>
</NavigationContainer>
</TabContextProvider>
</>
);
}
And the Simplified ActionButton Component:
export default function ActionButton({ opened, toggleOpened }) {
return (
<View style={styles.container}>
<View style={styles.box}>
<TouchableWithoutFeedback
onPress={toggleOpened}
style={styles.actionButton}
>
/* With an Animated View inside */
</TouchableWithoutFeedback>
</View>
</View>
);
}
Basically, **toggleOpened **should switch the value of the variable **opened **between true and false. So the **AnimatedView **can work properly which solely depends on the value of opened.
Opened is readable in all of the components, no problem with that. But **toggleOpened **is not working at all. Any idea?
In order to use contexts properly, you need to have at least two components working together,one that renders the Provider and one descendant who then uses that context.
You are trying to provide and use the context at the same time,try to move the consumer component one position down to the hierarchy.
For example in your App.js you can create a consumer component to wrap your ActionButton,then pass the context to it as you did :
export default function App() {
return (
<>
<TabContextProvider>
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen
name="Action"
component={ActionScreen}
options={{
tabBarButton: () => <ActionButtonWrapper />
}}
/>
</Tab.Navigator>
</NavigationContainer>
</TabContextProvider>
</>
);
}
const ActionButtonWrapper = () => {
const { opened, toggleOpened } = useContext(TabContext);
return (
<>
<ActionButton
opened={opened}
toggleOpened={toggleOpened}
/>
</>
);
};
However,i would just use the context directly within ActionButton,after all,passing props down to children is what we want to avoid by using context,right?
I have created for you a snippet to see how we can properly use context

How to assign different click function to each face of the cube in react three fibre

Hi I have a simple Cube component made with react three fibre.
import {useState,useRef} from 'react';
import { useFrame } from '#react-three/fiber';
const Box = ({props}) =>{
const ref = useRef()
const [hovered, hover] = useState(false)
const boxClick = () =>{
alert('clicked the object')
}
return (
<mesh
{...props}
ref={ref}
onClick={boxClick}
scale={2}
onPointerOver={(event) => hover(true)}
onPointerOut={(event) => hover(false)}>
<boxGeometry args={[1.5, 1.5, 1.5]} />
<meshStandardMaterial color={hovered ? 'yellow' : 'orange'} />
</mesh>
)
}
export default Box;
I am importing the Box compoent in App.js and using it this way-
function App() {
const [boxes,setBoxes] = useState(0);
const box1Click = () =>{
setBoxes(() =>boxes+1)
alert(boxes)
}
return (
<Canvas className='main-canvas'>
<ambientLight intensity={0.5} />
<spotLight position={[5, 155, 10]} angle={0.15} penumbra={1} />
<pointLight position={[-100, -200, -100]} />
<Box position = {[0 ,0,0]} />
</Canvas>
);
}
export default App;
Right now I can click the cube and it runs the boxClick function properly. What I want to assign different click function to each face of the cube.
You can draw six planes and change the rotation or the position each time to construct a cube..
const Plane = (props) => {
const ref = useRef();
const texture = useLoader(THREE.TextureLoader, "/images/wood.png");
useFrame((state) => {
if (props.rotateX) ref.current.rotation.x = props.rotateX;
if (props.rotateY) ref.current.rotation.y = props.rotateY;
if (props.rotateZ) ref.current.rotation.z = props.rotateZ;
// ref.current.rotation.x += 0.01;
// ref.current.rotation.y += 0.01;
});
return (
<mesh ref={ref} {...props}>
<planeGeometry />
<meshBasicMaterial
color="red"
side={THREE.DoubleSide}
opacity={props.opacity}
transparent
visible={props.visible}
map={texture}
/>
</mesh>
);
};
And then add them to your scene
<Suspense fallback={null}>
<Box position={[3, 3, 0]} />
<Plane
args={[2, 2]}
position={[0.5, 0, 0.5]}
rotateX={Math.PI / 2}
opacity="0.5"
visible={true}
/>
<Plane
args={[2, 2]}
position={[0.5, 0.5, 1]}
opacity="0.8"
visible={true}
/>
<Plane
args={[2, 2]}
position={[0, 0.5, 0.5]}
rotateY={Math.PI / 2}
opacity="1"
visible={true}
/>
{/** ... */}
</Suspense>

How to filter on-click in react three js?

So I explore 2 cubes sample. I want to click only on one cube at a time. Yet when I click on a cube that has a cube right behind the current one both get clicked. How to force click and hover only on the nearest one?
In other words clicking\hovering on cube A I want cube B not to be clicked
My code sample:
import React, { useRef, useState } from 'react'
import { Canvas, useFrame } from '#react-three/fiber'
function Box(props) {
// This reference will give us direct access to the mesh
const mesh = useRef()
// Set up state for the hovered and active state
const [hovered, setHover] = useState(false)
const [active, setActive] = useState(false)
// Rotate mesh every frame, this is outside of React without overhead
useFrame(() => {
mesh.current.rotation.x = mesh.current.rotation.y += 0.01
})
return (
<mesh
{...props}
ref={mesh}
scale={active ? 1.5 : 1}
onClick={(e) => setActive(!active)}
onPointerOver={(e) => setHover(true)}
onPointerOut={(e) => setHover(false)}>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
</mesh>
)
}
export default function App() {
return (
<Canvas>
<ambientLight intensity={0.5} />
<spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} />
<pointLight position={[-10, -10, -10]} />
<Box position={[1.2, 0.5, 0]} />
<Box position={[1.2, 0, 0]} />
</Canvas>
)
}
Solved it here
main diff:
function intersectionsFilter(intersections) {
return intersections?.length ? [intersections[0]] : intersections
}
export default function App() {
return (
<Canvas raycaster={{ filter: intersectionsFilter }}>

How to use ternary operator to return jsx using react?

I want to hide a component if the user is in the "/items" page.
below is my code,
function Main() {
const isAdmin = getUser();
return(
<Switch>
<Route
exact
path="/items"
render={routeProps => (
<Layout>
{isAdmin ? <Items {...routeProps} />: <NotFound/>}
</Layout>
)}
/>
//other Routes
</Switch>
);
}
const Layout: React.FC = ({ children }) => (
<>
<TopBar />
{children}
<BottomBar />
</>
);
Now when the user is in /items page I don't want the TopBar and BottomBar to be displayed.
how can I do it? could someone help me with this? thanks.
Change your Layout component as below:
const Layout: React.FC = ({ children }) => {
const history = useHistory();
const isItemsPath = history.location.pathname.includes("/items");
return (
<>
{!isItemsPath && <TopBar />}
{children}
{!isItemsPath && <BottomBar />}
</>
);
}

React Hooks: toggle element on map function

See that loop loading two components. I would like to display only <Image /> by default, but when I click this element, I want it to turn into <YouTube /> (only the one I press, the others are still <Image />). I can do this on a class component, but I want to use a hook
export const MusicVideos = () => {
const [selectedVideo, setVideo] = useState(0);
return (
<Wrapper>
{videos.map(video => (
<div key={video.id}>
<Image src={video.image} hover={video.hover} alt="thumbnail" />
<YouTube link={video.link} />
</div>
))}
</Wrapper/>
);
};
you can bind onClick for your image and setVideo to video.id and compare with video.id to render image or video.
export const MusicVideos = () => {
const [selectedVideo, setVideo] = useState(0);
return (
<Wrapper>
{videos.map(video => (
{selectedVideo !== video.id ?
<Image onClick={() => setVideo(video.id) src={video.image} hover={video.hover} alt="thumbnail" /> :
<YouTube link={video.link} />
))}
</Wrapper/>
);
};
Create a component like this and pass it to the loop;
const YouTubeToggle = (video) => {
const [selectedVideo, setVideo] = useState(0);
return (
<div key={video.id}>
{selectedVideo == 0 &&
<Image src={video.image} onClick={() => setVideo(!selectedVideo)} hover={video.hover} alt="thumbnail" />
}
{selectedVideo != 0 &&
<YouTube link={video.link} />
}
</div>
);
}
export const MusicVideos = () => {
const [selectedVideo, setVideo] = useState(0);
return (
<Wrapper>
{videos.map(video => (
<YouTubeToggle video={video} />
))}
</Wrapper/>
);
};

Categories

Resources