Handling events across different exports with React - reactjs

So I wanted to create a popup div that would slide from the side when an object has been selected and then exit when the object is re selected. I also want to create an exit button that would also close the div. I can pretty much understand how to do this except that I want to reuse this div component which is why I have kept it as an export in a different javascript file. This is where the issue is as I am having trouble handling the events across the files.
Here is my code:
/*Popup div export*/
export default () => {
const [toggle, set] = useState(true);
const { xyz } = useSpring({
from: { xyz: [-1000, 0, 0] },
xyz: toggle ? [0, 0, 0] : [-1000, 0, 0]
});
return (
<a.div
style={{
transform: xyz.interpolate(
(x, y, z) => `translate3d(${x}px, ${y}px, ${z}px)`
)
}}
className="expand"
>
<Link to={link}>
<a.button>Next</a.button>
</Link>
<button onClick={() => set(!toggle)}>Exit</button>
</a.div>
);
};
/*This is where I am implementing the export*/
<Canvas>
<Suspense fallback={<Fallback />}>
<Cube position={[-1.2, 0, 0]} onClick={e => <Toggle />} /> <---/*Here is the click event where I call the div*/-->
<Cube position={[1.2, 0, 0]} />
</Suspense>
</Canvas>
);
}
I have tried changing the styling to make the display 'hidden' and 'block' but this doesn't work as it doesn't show the slide in animation it just pops up. Furthermore, If I try to manipulate this separately, for example, create a click event within the canvas to make the div appear with react-spring, if I try to use the exit button, the click event doesn't work anymore.
Here is my sandbox to show what is happening. : (p.s sorry if this all seems confusing)
The codes are within Page1.js and toggle.js
https://codesandbox.io/s/sad-goldberg-pmb2y?file=/src/toggle.js:250-326
Edit:
simpler sandbox visual:
https://codesandbox.io/s/happy-chatelet-vkzjq?file=/src/page2.js

Your example is a bit confusing to follow, a simpler reproduction would be nice. That said, if I understand the overall goal, I think you want to store some global state (perhaps in your App.js component) that has some sort of state about the sidebar being visible.
For example:
function App() {
const [sidebarVisible, setSidebarVisible] = React.useState(false)
const toggleSidebar = () => setSidebarVisible(!sidebarVisible)
return (
<Router>
<Switch>
<Route path="/page1">
<Page1 toggleSidebar={toggleSidebar} />
</Route>
<Route path="/page2">
<Page2 toggleSidebar={toggleSidebar} />
</Route>
<Route path="/">
<Start toggleSidebar={toggleSidebar} />
</Route>
</Switch>
</Router>
)
}
function Page1({ toggleSidebar }) {
return <Toggle toggleSidebar={toggleSidebar} />
}
function Toggle({ toggleSidebar }) {
return <button onClick={toggleSidebar}>Toggle</button>
}
This is just to give you ideas, you could of course pass the setSidebarVisible function or make another function that stores some sort of state about what should show on the sidebar.
You could also use something like Redux or React Context to pass down state/actions into your components.
Hope this helps somewhat 👍🏻

Related

Unexpected behavior of React render props

I've come up with the following code using React Transition Group:
const Transition = ({ elements, selectKey, render: Element, ...props }) => (
<TransitionGroup {...props}>
{elements.map((element) => (
<CSSTransition
key={selectKey(element)}
timeout={1000}
className="transition-slide"
>
<Element {...element} />
</CSSTransition>
))}
</TransitionGroup>
)
The key part here is that Transition component receives the render prop and renders it applying some transitions.
The way I expected for this to work:
<Transition render={(props) => <Toast {...props} />} />
But this code does not work as I expected: the transition of the next element interrupts the transition of the previous one.
However, this code works just fine:
const Element = (props) => <Toast {...props} />
// ...
<Transition render={Element} />
How can I fix this issue without putting the desired render-prop into a separate component?
Codesandbox: Example sandbox. The sandbox presents a non-working option with animation interruption. To get a working version, you need to uncomment lines 16 and 30 in the /Toasts/index.js file
P.S. I can't just use render={Toast} because I need to do ({id}) => <Toast dismiss={() => {deleteToast(id)}} />. I omitted this detail in order to simplify the understanding of the problem.
If you don't want to put the render function into another component, putting it into a useCallback() solved it for me.
const Toasts = () => {
const [toasts, addToast] = useToasts();
const Element = useCallback((props) => <Toast {...props} />, []);
return (
<div>
<button onClick={addToast}>Add toast</button>
<List>
<Transition
elements={toasts}
selectKey={({ id }) => id}
render={Element}
/>
</List>
</div>
);
}
(I don't quite understand the origin of the issue, but it has to do something with the function references.)

What's the best way to create a hamburger menu from scratch in Reactjs without using Hooks?

I'm trying to create a hamburger menu within React using state and onClick, from scratch. I have no experience with React Hooks so am trying to avoid that. The solution I have right now works, but is slow and glitchy and I have a feeling the code is quite flawed.
I deployed it anyways to test it out in live, and it's not working well at all in mobile chrome browser (https://vigilant-leavitt-a88a0e.netlify.app/). (though at least the background-color works when inspecting on a browser at mobile dimensions).
Can anyone tell me what I'm doing wrong here, or how this could be cleaned up? I have app.js feeding props to header.js.
Thank you!
App.js
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
menuOpen: false
};
}
componentDidMount() {
this.setState({
menuOpen: false
});
}
handleMenuClick = () => {
this.setState({ menuOpen: true });
}
handleCloseMenuClick = () => {
this.setState({ menuOpen: false });
}
render() {
return (
<>
<Router>
<Header
menuOpen={this.state.menuOpen}
handleMenuClick={this.handleMenuClick}
handleCloseMenuClick={this.handleCloseMenuClick} />
<Switch>
<Route path="/resources"><Resources /></Route>
<Route path="/team"><Team /></Route>
<Route path="/faq"><Faq /></Route>
<Route path="/fees"><Fees /></Route>
<Route path="/"><Home /></Route>
</Switch>
<Footer />
</Router>
</>
);
}
}
Header.js
<div className="header__navburger">
{props.menuOpen === false ?
(<img
className="header__hamburger-icon"
alt="Menu"
src={menuicon}
onClick={() => props.handleMenuClick()}
/>) :
(<div className="header__navburger-close">
<img
className="header__close-icon"
alt="Close Menu"
src={closeicon}
onClick={() => props.handleCloseMenuClick()}
/>
<nav className="header__nav-list">
<Link to={`/home`} className="header__nav-link"
onClick={() => props.handleCloseMenuClick()}>Home</Link>
<Link to={`/resources`} className="header__nav-link"
onClick={() => props.handleCloseMenuClick()}>Patient Resources</Link>
<Link to={`/team`} className="header__nav-link"
onClick={() => props.handleCloseMenuClick()}>Meet the Team</Link>
<Link to={`/faq`} className="header__nav-link"
onClick={() => props.handleCloseMenuClick()}>FAQ</Link>
<Link to={`/fees`} className="header__nav-link"
onClick={() => props.handleCloseMenuClick()}>Fees</Link>
</nav>
</div>
)}
</div>
Did you mean icons of humburger menu changing slowly and glitchy? I've looked at the site and menu looks quite good for me.
First of all, there is no need in setState in your componentDidMount() as you should have the state set by this moment in constructor where initialization happens. This would force rendering the component twice with same state and be the reason that something works a little slow.
Second, do you use images for humburger menu icons? Downloading images takes some time as well, and I think that's why when clicking burger menu I don't see menu cross just in time. There are other ways to make menu like this, for exapmle it may be just
<div class="burger-menu">
<span></span>
</div>
and styles for span, span::before and ::after for white lines. You can also transform these lines to make a cross of it. I suggest this solution would allow things to work a little faster

Calling props.myFunction() inside a function in react functional component

I am passing a function to a child component in React and it works perfectly when I call it directly in onClick of a button. But then when I move it into a separate function and call the function from onClick, it no longer works. I can verfity the function is being called with a log statement, but props.myFunction() never gets called. I've encountered this a few times now in React and it always confuses me.
I've looked at some other questions like this one, but its still not helping.
React: Can't call prop function when it is inside of another function?
This code works - it sets loggedIn to true in the parent when the button is clicked
export default function SignupModal(props) {
return (
<div class="main-block">
<button
className="create-account-button"
href="/"
onClick={props.setIsLoggedIn(true)}
>
Create Account
</button>
</div>
);
}
this code doesn't set loggedIn to true - but the function still gets called
export default function SignupModal(props) {
const createAccount = () => {
console.log("this gets logged");
//but this doesn't get called
props.setIsLoggedIn(true);
};
return (
<div class="main-block">
<button
className="create-account-button"
href="/"
onClick={createAccount}
>
Create Account
</button>
</div>
);
}
can anyone tell me why?
here is what I'm trying to do in the parent, maybe a little unorthodox to render routs like this but it's for a splash page - also as mentioned it works perfectly in onClick()
const [isLoggedIn, setIsLoggedIn] = useState(false);
return (
<>
{isLoggedIn ? (
<>
<SearchBar onLocationChange={onLocationChange}></SearchBar>
<NavBar></NavBar>
<Switch>
<Route exact path="/quik">
<Pins
mapRef={mapRef}
pinnedLocationIds={pinnedLocationIds}
pinsRemaining={pinsRemaining}
pushPinnedLocation={pushPinnedLocation}
usePin={usePin}
mapCenter={mapCenter}
setMapCenter={setMapCenter}
matches={matches}
setMapZoom={setMapZoom}
mapZoom={mapZoom}
changeStatus={changeStatus}
/>
</Route>
<Route path="/potentials">
{/* dont forget,
the props for 'Potentials' must also pass
through 'potentials in the 'Pins' component! */}
<Potentials
pinsRemaining={pinsRemaining}
matches={matches}
changeStatus={changeStatus}
/>
</Route>
<Route path="/connects">
<Connects matches={matches} recentMatchId={recentMatchId} />
</Route>
<Route path="/profile" component={Profile} />
</Switch>
</>
) : (
<Route exact path="/quik">
<Landing setIsLoggedIn={() => setIsLoggedIn}></Landing>
</Route>
)}
</>
);
Just to demonstrate the difference between your two cases, take this example. When I call a function immediately in the returned JSX code, it fires as soon as the element mounts. However, in the second button, I have to click it before the logging will happen.
function App() {
return (
<div>
<button onClick={console.log("foo")}>Immediate log</button>
<button onClick={() => console.log("bar")}>Must click to log</button>
</div>
);
}
ReactDOM.render(<App />, document.getElementById('main'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="main"></div>
Edit: In the case of your code, you're actually setting setIsLoggedIn to a function that returns a function when you pass it to your Landing component:
<Landing setIsLoggedIn={() => setIsLoggedIn} ></Landing>
What you want to do instead is this:
<Landing setIsLoggedIn={setIsLoggedIn} ></Landing>
Now setIsLoggedIn is just a function and your second example will work (your first example will fire immediately and not work how you intend).

useState hook being called inside map function causing infinite loop

I'm currently building a dynamic form engine and I want to display results from the redux store when the Answer Summary component is rendered. The way I figured would be best to do this would be to having a 'complete' status and set it to true once the answerSummary component is loaded, but doing this within the map function does not work and throws the infinite loop react error.
Code is here:
function App() {
let [complete, setComplete] = useState(false);
return (
<div>
<h1>Form App Prototype</h1>
<Router>
<Switch>
{Object.values(Database.steps).map(step => {
step.name === 'answerSummary' ? setComplete(true) : setComplete(false);
return (
<Route exact path={`/${step.name}`} render={() =>
<Step step={step} />
}
/>
)
})}
</Switch>
</Router>
<br></br>
<div style={{display: complete? 'block' : 'none'}}><StoreVisual/></div>
</div>
);
}
export default App;
EDIT: I know you aren't able to setState inside the render - I've written it this way as a way to try and convey what I want to be able to do
My understanding of your problem is that you are trying to display results after the answer summary component is mounted.
You can achieve this by using the useEffect hook which runs when the component mounts. https://reactjs.org/docs/hooks-effect.html
If you only want to render <StoreVisual/> when they are on the last step it might be easier to set up a state hook for the index of the step people are on.
const [index, setIndex] = useState(0);
Every time someone progresses a step increment this value.
You would have to pass setIndex, or whatever you call your setter, into the Step component to do this.
Then you can render <StoreVisual/> with a conditional, like so...
<div>
<h1>Claimer Form App Prototype</h1>
<Router>
<Switch>
{Object.values(Database.steps).map(step =>
<Route exact
path={`/${step.name}`}
render={() => <Step step={step} /> }/> )}
</Switch>
</Router>
<br></br>
{Database.steps[index] === 'answerSummary' && <StoreVisual/>}
</div>
This approach also affords you a simple way to let people start in the middle of the form. Say you want to let people save half-finished forms in the future, you just change/update the default value of index hook.
Instead of running that code inline in your return, build the array in your function logic:
function App() {
let [complete, setComplete] = useState(false);
// build an array of Route components before rendering
// you should also add a unique key prop to each Route element
const routes = Object.values(Database.steps).map(step => {
step.name === 'answerSummary' ? setComplete(true) : setComplete(false);
return <Route exact path={`/${step.name}`} render={() => <Step step={step} />} />
})
return (
<div>
<h1>Claimer Form App Prototype</h1>
<Router>
<Switch>
// render the array
{[routes]}
</Switch>
</Router>
<br></br>
<div style={{display: complete? 'block' : 'none'}}><StoreVisual/></div>
</div>
);
}
export default App;
I don't think you need to call setComplete(false) based on this logic, so you should probably replace your ternary with an if statement:
if (step.name === 'answerSummary') {
setComplete(true)
}
and avoid making unnecessary function calls
You cannot set state inside render which cause infinite loop.[Whenever state is changed, component will re-render(calls render function)]
render()=>setState()=>render()=>setState().......infinite
WorkAround:
<div style={{display: this.props.location.pathname=='answerSummary'? 'block' : 'none'}}><StoreVisual/></div>

React Navigation Global FAB

I am using react-navigation v5 and ended up with App.js like this
const MainStack = createStackNavigator();
export default () => (
<NavigationNativeContainer>
<MainStack.Navigator
screenOptions={{
header: () => <AppbarHeader />,
}}
>
<MainStack.Screen name="index" component={index} />
<MainStack.Screen name="alternate" component={alternate} />
</MainStack.Navigator>
</NavigationNativeContainer>
);
I would like to add a Floating Action(FAB) Button that would be visible on the bottom of both the index and alternate page that would allow the user to show a Modal that is currently omitted. Right now I feel like the only solution is to put my FAB component inside both the index and alternate components but this doesn't seem right because it shouldn't re-render and transition in with the page. It should float above the page transition. I feel like it should be more of a global navigator of some sort but I am not sure how that should work with the existing StackNavigator shown above.
I am Looking forward to any solutions provided.
As #satya164 noted, you can put FAB in your root component. In addition, to access to navigation actions, you can use a ref to your navigation container.
const App = () =>{
const ref = React.useRef(null);
return (
<>
<NavigationNativeContainer ref={ref}>
// ...
</NavigationNativeContainer>
<FAB onPress={() => ref.current && ref.current.navigate('A SCREEN')}/>
</>
);
If you really want it to be global, put it in your root component and not inside a screen.
const App = () => (
<>
<NavigationNativeContainer>
// ...
</NavigationNativeContainer>
<FAB />
</>
);

Resources