I have a React project with two separate components, a Header and a Sidebar. On the header there is a button which should collapsed the sidebar. I tried setting up a boolean const that would toggle a class in the Sidebar component, making the Sidebar collapsed. The only way I can think of doing this is a universal constant. Is this possible in React and/or is there a better/other way of achieving this other than merging the two components into one?
You can use context api using useContext hook. The shared state can be defined in the parent component like your layout and accessed in sidebar and header..
Hope it helps
You probably want to move the state to the parent, something along those lines:
function NavBar() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<Sidebar className={isOpen && "isOpen"} />
<NavToggle onClick={() => setIsOpen(true)} />
</>
);
}
function NavToggle(props) {
return <button onClick={props.onClick}>Toggle nav!</button>
}
Related
I'm trying to create rich React component as popover content.
If I use example with simple const popover (https://react-bootstrap.netlify.app/components/overlays/#examples-1) everything works fine.
Problem
But custom react component fails to position itself. It appears on top left of the screen
const MyPopover = React.forwardRef((props, ref) => {
return <Popover ref={ref} placement={"bottom"}>
<Popover.Header as="h3">
<Form.Control></Form.Control>
</Popover.Header>
<Popover.Body>
<strong>Holy guacamole!</strong> Check this info.
</Popover.Body>
</Popover>
})
const PopoverChooser = ({children, container}) => {
const _refTarget = useRef(null)
return <>
<OverlayTrigger
trigger="click"
placement="bottom"
overlay={<MyPopover ref={_refTarget}/>}
target={_refTarget.current}
>
{children}
</OverlayTrigger>
</>
}
export default PopoverChooser;
As you can see, I'v tried to use ref's, but it's doesn't help.
Question
How can it link popover to target button (in image as dropdown button and in code as {children}).
Or should I position MyPopover manually (by checking button ID and assigning position.. etc.?)
Completely different approach to dig in..?
Your approach to forward the ref was right. What you actually forgot is to also inject props. According to the documentation:
The and components do not position themselves.
Instead the (or ) components, inject ref and
style props.
https://react-bootstrap.netlify.app/components/overlays/#overview
So what you need to do is to spread the props like this:
const MyPopover = React.forwardRef((props, ref) => {
return (
<Popover ref={ref} {...props}>
https://codesandbox.io/s/trusting-sid-0050g9
Why does defining a React functional component inside another functional component break CSS transitions?
function Doohick({isOpen}: {isOpen: boolean}) {
const style = {
transition: 'opacity 2s ease',
...(isOpen ? {opacity: 1} : {opacity: 0})
}
return (
<div style={style}>
Doohick!!!
</div>
)
}
function Parent() {
const [open, isOpen] = useState(false)
return (
<>
<button onClick={() => setIsOpen(!isOpen)}>Toggle Doohick</button>
<Doohick isOpen={isOpen} />
</>
)
}
If I define Doohick outside of Parent, as above, everything works great. If I move the definition inside Parent, with no other changes, my CSS transitions break. Other CSS properties are fine.
Why does defining a functional component inside another functional component break CSS transitions?
Complicated Explanation of Why I Want To Do This
I hear you asking: why would I want to do that? I'll tell you, but bear in mind you don't need to know any of this to understand the specific problem.
I want to encapsulate the Doohick state in a custom hook:
function useDoohick() {
const [isOpen, setIsOpen] = useState(false)
const ToggleButton =
<Button onClick={() => setIsOpen(!isOpen)}>Toggle Doohick</Button>
const Doohick = <MyDoohick show={isOpen}/>
return {ToggleButton, Doohick}
}
function Parent() {
const {Doohick, ToggleButton} = useDoohick()
return (
<>
{ToggleButton}
{Doohick}
</>
)
}
But I also want the Parent to be able to pass its own props into Doohick or ToggleButton. I can almost achieve that that like this:
function useDoohick() {
const [isOpen, setIsOpen] = useState(false)
const ToggleButton = ({text}) =>
<Button
onClick={() => setIsOpen(!isOpen)}
>
{text}
</Button>
const Doohick = () =>
<MyDoohick show={isOpen} />
return {ToggleButton, Doohick}
}
function Parent() {
return (
<>
<ToggleButton text='Burninate' />
<Doohick />
</>
)
}
This works as advertised: ToggleButton renders with the expected label and controls whether or not Doohick is shown. But this pattern breaks some CSS styles (specifically, transitions) I have defined on Doohick. Other styles are fine.
I can still call it like this:
function Parent() {
return (
<>
{ToggleButton({text: 'Burninate'})}
{Doohick()}
</>
)
}
...and the transitions work correctly. But I would much prefer the standard JSX syntax here:
<ToggleButton text='Burninate />
Clearly, <Doohick /> and Doohick() are different. But what is it about the former that breaks CSS transitions here?
The root of the problem boils down to defining the custom components inside the Parent. The hook itself is irrelevant. But this pattern of encapsulating state in a custom hook while returning a customizable component is really powerful and almost works, so I'm hoping there's a way it can be saved.
TL;DR
Why does defining a component within another component break my CSS transitions (and possibly other styles I haven't found yet)? How can I get around this while still calling my nested component with JSX-style syntax?
Defining a component inside another component will always result in issues like this. Every time the outer component renders, you create a brand new definition of the inner component. It may have the same text as the one from the previous render, but it's a different function in memory, so as far as react can tell it's a different type of component.
The component type is the main thing that react looks for when reconciling changes. Since the type changed, react is forced to unmount the old component and then mount the new one. So rather than having a <div> on the page who's style is changing, you have a div with some style, then it gets deleted and an unrelated div gets put onto the page. It may have a different style, but since this is a brand new div, the transition property won't do anything.
Hi I have a component like this :
const Comp = (props) => {
return(
<div>
data here
</div>
)
}
and I want to use framer motion like this:
const Container = () => {
return(
<motion.Comp variants={variants} someProp="someProp"/>
)
}
The reason I want to do this is that I don't think making a lot of div as a wrapper is a great idea.
I could use motion.div in "Comp" component but some of my app component is made that I don't need animating all of them in my whole app. I just want a solution to add animations to the first element in the component.
Also I searched and I found a solution "motion(Comp)", but it's not working.
From the docs (https://www.framer.com/docs/component/#custom-components), you can wrap any component with motion() to use it as if it were a framer-motion component. You mentioned that you found that solution but that it's not working, and without any more details about it I can't give you a specific reason why. But most likely you're forgetting to pass the ref through to your component, like this:
const Foo = React.forwardRef((props, ref) => <div ref={ref} />)
The ref is the way that framer-motion will identify which element on the page is the one it should be animating, so it needs to be passed to the correct element. Once that's done, you can wrap it with motion:
const MotionFoo = motion(Foo)
Keep in mind that regardless of how many elements your custom component has, only the one that has the ref passed to it will be animated. For example:
const BusyFoo = React.forwardRef((props, ref) => (
<div>
<div ref={ref}>I will be animated!</div>
<div>I won't be animated. :(</div>
</div>
)
I am trying to create a Stateful class in which you can call methods such as createHeaderButton() where after calling it would update the state and re-render with these new updates in the component.
Im using Material-UI and so most of their styling utilizes Reacts hook API which of course classes cant use. Ive tried to get around this by using;
export default withStyles(useStyles)(HeaderBar)
Which exports the class separately with the Styles(withStyles(useStyles) useStyles as the defined styles) And the class(HeaderBar). Now the only issue is that i need to access the styles in my class. Ive found a JS example online that wont work for me because of the strong typed syntax of TS. Additionally When initializing my Class component in other places i try to get the ref=(ref:any)=>{} And with that call the create button methods when i get a response from my server, Which doesnt work because of this new way of exporting the class component!
Thanks for the help, Heres my component class: https://pastebin.pl/view/944070c7
And where i try to call it: https://pastebin.com/PVxhKFHJ
My personal opinion is that you should convert HeaderBar to a function component. The reason that it needs to be a class right now is so you can use a ref to call a class method to modify the buttons. But this is not a good design to begin with. Refs should be avoided in cases where you can use props instead. In this case, you can pass down the buttons as a prop. I think the cleanest way to pass them down is by using the special children prop.
Let's create a BarButton component to externalize the rendering of each button. This is basically your this.state.barButtons.forEach callback, but we are moving it outside of the HeaderBar component to keep our code flexible since the button doesn't depend on the HeaderBar (the header bar depends on the buttons).
What is a bar button and what does it need? It needs to have a label text and a callback function which we will call on click. I also allowed it to pass through any valid props of the material-ui Button component. Note that we could have used children instead of label and that's just down to personal preference.
You defined your ButtonState as a callback which takes the HTMLButtonElement as a prop, but none of the buttons shown here use this prop at all. But I did leave this be to keep your options open so that you have the possibility of using the button in the callback if you need it. Using e.currentTarget instead of e.target gets the right type for the element.
import Button, {ButtonProps as MaterialButtonProps} from "#material-ui/core/Button";
type ButtonState = (button: HTMLButtonElement) => void;
type BarButtonProps = {
label: string;
callback: ButtonState;
} & Omit<MaterialButtonProps, 'onClick'>
const BarButton = ({ label, callback, ...props }: BarButtonProps) => {
return (
<Button
color="inherit" // place first so it can be overwritten by props
onClick={(e) => callback(e.currentTarget)}
{...props}
>
{label}
</Button>
);
};
Our HeaderBar becomes a lot simpler. We need to render the home page button, and the rest of the buttons will come from props.childen. If we define the type of HeaderBar as FunctionComponent that includes children in the props (through a PropsWithChildren<T> type which you can also use directly).
Since it's now a function component, we can get the CSS classes from a material-ui hook.
const useStyles = makeStyles({
root: {
flexGrow: 1
},
menuButton: {
marginRight: 0
},
title: {
flexGrow: 1
}
});
const HeaderBar: FunctionComponent = ({ children }) => {
const classes = useStyles();
return (
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<HeaderMenu classes={classes} />
<Typography variant="h6" className={classes.title}>
<BarButton
callback={() => renderModule(<HomePage />)}
style={{ color: "white" }}
label="Sundt Memes"
/>
</Typography>
{children}
</Toolbar>
</AppBar>
</div>
);
};
Nothing up to this point has used state at all, BarButton and HeaderBar are purely for rendering. But we do need to determine whether to display "Log In" or "Log Out" based on the current login state.
I had said in my comment that the buttons would need to be stateful in the Layout component, but in fact we can just use state to store an isLoggedIn boolean flag which we get from the response of AuthVerifier (this could be made into its own hook). We decide which buttons to show based on this isLoggedIn state.
I don't know what this handle prop is all about, so I haven't optimized this at all. If this is tied to renderModule, we could use a state in Layout to store the contents, and pass down a setContents method to be called by the buttons instead of renderModule.
interface LayoutProp {
handle: ReactElement<any, any>;
}
export default function Layout(props: LayoutProp) {
// use a state to respond to an asynchronous response from AuthVerifier
// could start with a third state of null or undefined when we haven't gotten a response yet
const [isLoggedIn, setIsLoggedIn] = useState(false);
// You might want to put this inside a useEffect but I'm not sure when this
// needs to be re-run. On every re-render or just once?
AuthVerifier.verifySession((res) => setIsLoggedIn(res._isAuthenticated));
return (
<div>
<HeaderBar>
{isLoggedIn ? (
<BarButton
label="Log Out"
callback={() => new CookieManager("session").setCookie("")}
/>
) : (
<>
<BarButton
label="Log In"
callback={() => renderModule(<LogInPage />)}
/>
<BarButton
label="Sign Up"
callback={() => renderModule(<SignUpPage />)}
/>
</>
)}
</HeaderBar>
{props.handle}
</div>
);
}
I believe that this rewrite will allow you to use the material-ui styles that you want as well as improving code style, but I haven't actually been able to test it since it relies on so many other pieces of your app. So let me know if you have issues.
I'm very very new with React. Actually I started to learn today.
I want to build a single sidebar component in a website.
I toggle a sidebar using UseState and a function called openHandler that verifies of the sidebar is already active.
I'm wondering how should I approach the toggle inside of the sidebar. How should I change the state of the sidebar if all the handle is done in the main file (App.js)
I'm really sorry if this question don't make sense.
Here is a SandBox example of what I'm talking about.
https://codesandbox.io/s/hopeful-framework-c4k1h
If someone know what should I learn to play with that would be great.
Thanks in advance
you can pass the main handler to sidebar via props and bind it to insider toggle.
appjs
...
<CartBox openHandler= {openHandler} className={ToggleCartState} />
...
cartBox.js
...
<Toggle click={props.openHandler} />
...
Nice to read https://reactjs.org/docs/lifting-state-up.html
I assume you want to be able to change the state of the sidebar by clicking a button from another sibling / child of a sibling component.
if that's the case you'll need to put the useState hook in the higher level parent, then pass the state / an it's setter method as a prop to the children that will use it.
here is an example of what I mean.
Parent Component
function parent() {
// the sidebar state
const [sidebar, setSidebar] = useState(false);
// helper function that toggles state
function toggle() {
setSidebar(!sidebar);
}
return (
<section className="Parent">
{ /* Conditional Render */
sidebar ?
<Navbar stateManager={{toggle}} />
: <HamburgerIcon stateManager={{toggle}} />
}
</section>
)
Navbar / HumburgerIcon
function Navbar({stateManager}) {
// you now passed state and it's set method to the child
const {toggle} = stateManager;
return (
<div onClick={toggle}>
component content
</div>
}
You can put them all in same file and still do the same thing.