react-popper: re-position using scheduleUpdate - javascript

I'm using React-popper to show a date picker element after clicking a button.
JSX
<Manager>
<Reference>
{({ ref }) => (
<button ref={ref} onClick={this.onDateRangeBtnClick}>click to show</button>
)}
</Reference>
{ReactDOM.createPortal(
<Popper placement="auto-end" >
{({ ref, style, placement, arrowProps, scheduleUpdate }) => (
<div className={`dayPickerOverlay ${this.state.showDatePicker ? "" : "hidden"}`} ref={ref} style={style} data-placement={placement}>
<DateRangePicker />
</div>
)}
</Popper>,
document.querySelector('#root')
)}
</Manager>
When onDateRangeBtnClick is called after the button was clicked, I want to re-position the Popper element by calling scheduleUpdate method, but I do not know how to approach this.
How can I expose that specific scheduleUpdate to be called within the onDateRangeBtnClick or alternatively how can I conditionally call a function (scheduleUpdate for this matter) within JSX itself?

I would split the popper part into its own component and take advantage of the React lifecycle hooks.
Inside componentDidUpdate you can check if the open state changed, and trigger the scheduleUpdate accordingly.
// PopperElement.js
export default class PopperElement extends React.Component {
componentDidUpdate(prevProps) {
if (this.props.open && this.props.open !== prevProps.open) {
this.props.scheduleUpdate();
}
}
render() {
return (
<div className={`dayPickerOverlay ${this.state.showDatePicker ? "" : "hidden"}`} ref={ref} style={style} data-placement={placement}>
<DateRangePicker />
</div>
);
}
}
// App.js
<Manager>
<Reference>
{({ ref }) => (
<button ref={ref} onClick={this.onDateRangeBtnClick}>click to show</button>
)}
</Reference>
{ReactDOM.createPortal(
<Popper placement="auto-end">
{({ ref, style, placement, arrowProps, scheduleUpdate }) => (
<PopperElement open={this.state.open} scheduleUpdate={scheduleUpdate} />
)}
</Popper>,
document.querySelector('#root')
)}
</Manager>
If you prefer a more concise approach, I think I'd use react-powerplug this way:
import { Manager, Popper, Reference } from 'react-popper';
import { State } from 'react-powerplug';
const App = () => (
<Manager>
<Popper>
{({ ref, style, scheduleUpdate }) => (
<State initial={{ open: false }} onChange={scheduleUpdate}>
{({ state, setState }) => (
<Fragment>
<Reference>
{({ ref }) => (
<button
ref={ref}
onClick={() => setState({ open: true }})
>click to show</button>
)}
</Reference>
{open && <YourContent ref={ref} style={style} />}
</Fragment>
)}
</State>
)}
</State>
</Manager>
);
I avoided to repeat the React.createPortal part for conciseness, it should be in place of YourContent.

Related

React table rowProps prop doesnt log anything

[Basically, we're using this from one of our libraries. But the thing is while using it through our package the rowProps prop doesn't work]
(https://i.stack.imgur.com/fER2B.png).
import { AdvancedTable } from "ims-ui-kit";
function Table(props) {
console.log(props)
return <AdvancedTable {...props} />;
}
export default Table;
Here is another screenshot of the prop passing through the table we are using. It doesn't log anything.
[enter image description here]
<>
{alert}
<div className="content">
<ReactTable
// hasBulkActions={true}
data={data}
filterable
{...rest}
resizable={false}
columns={columns.slice()}
defaultPageSize={10}
showPaginationTop
showPaginationBottom={false}
className="-striped -highlight"
rowProps={(row) => {
onclick = () => {
console.log("hello", row);
};
}}
/>
<Modal title="Risk management">
<RiskDetail isModalOpen={isOpen} />
</Modal>
</div>
</>

Sharing component states with useState() for active link in Nav

I'm trying to learn react and fairly new to the framework. I am trying to create a simple navbar component wih material-ui that is responsive (will show all links on medium devices and up, and open a side drawer on small devices). I have most of it setup to my liking, however, the issue I am currently having, is getting and setting the active link according to the page I am on.
It seems to works correctly on the medium devices and up, but when transitioning to a smaller device, the link is not updated correctly, as it will keep the active link from the medium screen set, while updating the side drawer active link.
Navbar.js
const Navbar = () => {
const classes = useStyles();
const pathname = window.location.pathname;
const path = pathname === '' ? '' : pathname.substr(1);
const [selectedItem, setSelectedItem] = useState(path);
const handleItemClick = (event, selected) => {
setSelectedItem(selected);
console.log(selectedItem);
};
return (
<>
<HideNavOnScroll>
<AppBar position="fixed">
<Toolbar component="nav" className={classes.navbar}>
<Container maxWidth="lg" className={classes.navbarDisplayFlex}>
<List>
<ListItem
button
component={RouterLink}
to="/"
selected={selectedItem === ''}
onClick={event => handleItemClick(event, '')}
>
<ListItemText className={classes.item} primary="Home" />
</ListItem>
</List>
<Hidden smDown>
<List
component="nav"
aria-labelledby="main navigation"
className={classes.navListDisplayFlex}
>
<ListItem
button
component={RouterLink}
to="/account/login"
selected={selectedItem === 'account/login'}
onClick={event => handleItemClick(event, 'account/login')}
>
<ListItemText className={classes.item} primary="Login" />
</ListItem>
<ListItem
button
component={RouterLink}
to="/account/register"
selected={selectedItem === 'account/register'}
onClick={event => handleItemClick(event, 'account/register')}
>
<ListItemText className={classes.item} primary="Register" />
</ListItem>
</List>
</Hidden>
<Hidden mdUp>
<SideDrawer />
</Hidden>
</Container>
</Toolbar>
</AppBar>
</HideNavOnScroll>
<Toolbar id="scroll-to-top-anchor" />
<ScrollToTop>
<Fab aria-label="Scroll back to top">
<NavigationIcon />
</Fab>
</ScrollToTop>
</>
)
}
SideDrawer.js
const SideDrawer = () => {
const classes = useStyles();
const [state, setState] = useState({ right: false });
const pathname = window.location.pathname;
const path = pathname === "" ? "" : pathname.substr(1);
const [selectedItem, setSelectedItem] = useState(path);
const handleItemClick = (event, selected) => {
setSelectedItem(selected);
console.log(selectedItem);
};
const toggleDrawer = (anchor, open) => (event) => {
if (
event &&
event.type === "keydown" &&
(event.key === "Tab" || event.key === "Shift")
) {
return;
}
setState({ ...state, [anchor]: open });
};
const drawerList = (anchor) => (
<div
className={classes.list}
role="presentation"
onClick={toggleDrawer(anchor, false)}
onKeyDown={toggleDrawer(anchor, false)}
>
<List component="nav">
<ListItem
button
component={RouterLink}
to="/account/login"
selected={selectedItem === "account/login"}
onClick={(event) => handleItemClick(event, "account/login")}
>
<ListItemText className={classes.item} primary="Login" />
</ListItem>
<ListItem
button
component={RouterLink}
to="/account/login"
selected={selectedItem === "account/register"}
onClick={(event) => handleItemClick(event, "account/register")}
>
<ListItemText className={classes.item} primary="Register" />
</ListItem>
</List>
</div>
);
return (
<React.Fragment>
<IconButton
edge="start"
aria-label="Menu"
onClick={toggleDrawer("right", true)}
>
<Menu fontSize="large" style={{ color: "white" }} />
</IconButton>
<Drawer
anchor="right"
open={state.right}
onClose={toggleDrawer("right", false)}
>
{drawerList("right")}
</Drawer>
</React.Fragment>
);
};
Code Sandbox - https://codesandbox.io/s/async-water-yx90j
I came across this question on SO: Is it possible to share states between components using the useState() hook in React?, which suggests that I need to lift the state up to a common ancestor component, but I don't quite understand how to apply this in my situation.
I would suggest to put aside for a moment your code and do a playground for this lifting state comprehension. Lifting state is the basic strategy to share state between unrelated components. Basically at some common ancestor is where the state and setState will live. there you can pass down as props to its children:
const Parent = () => {
const [name, setName] = useState('joe')
return (
<>
<h1>Parent Component</h1>
<p>Child Name is {name}</p>
<FirstChild name={name} setName={setName} />
<SecondChild name={name} setName={setName} />
</>
)
}
const FirstChild = ({name, setName}) => {
return (
<>
<h2>First Child Component</h2>
<p>Are you sure child is {name}?</p>
<button onClick={() => setName('Mary')}>My Name is Mary</button>
</>
)
}
const SecondChild = ({name, setName}) => {
return (
<>
<h2>Second Child Component</h2>
<p>Are you sure child is {name}?</p>
<button onClick={() => setName('Joe')}>My Name is Joe</button>
</>
)
}
As you can see, there is one state only, one source of truth. State is located at Parent and it passes down to its children. Now, sometimes it can be troublesome if you need your state to be located at some far GreatGrandParent. You would have to pass down each child until get there, which is annoying. if you found yourself in this situation you can use React Context API. And, for most complicated state management, there are solutions like redux.

Why I can't call useRef inside callback?

When I write this code I have an error:
React Hook "useRef" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function
What should I do with this code?
return ITEMS.map((item, i) => {
const elementRef = useRef(null);
return (
<div
ref={elementRef}
key={i}
>
<p>{item.name}</p>
<Wrapper>
{item.name === visibleItem && (
<Item
parentRef={elementRef}
/>
)}
</Wrapper>
</div>
);
}
Here are two possibilities, Either using useRef with an object/array, or using createRef as suggested by Yevgen Gorbunkov.
I'm not entirely sure as to the viability of these as the createRef option will create entirely new refs on each render, and the useRef option you'll need to make sure your keys/indexes are always the same.
const ITEMS = [{ name: "test" }, { name: "test2" }];
export default function App() {
const ref = useRef({});
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
{ITEMS.map((item, idx) => {
return (
<div key={idx} ref={element => (ref.current[idx] = element)}>
<p>{item.name}</p>
<Wrapper>
{item.name === visibleItem && (
<Item parentRef={ref.current[idx]} />
)}
</Wrapper>
</div>
);
})}
{ITEMS.map((item, idx) => {
const ref = createRef();
return (
<div key={idx} ref={ref}>
<p>{item.name}</p>
<Wrapper>
{item.name === visibleItem && <Item parentRef={ref} />}
</Wrapper>
</div>
);
})}
</div>
);
}

Modal is not Closing ReactJS Hooks Using Parent and Child Components

I am opening Model (child Component) on Button Click from Parent Component, it opens very well but its not closing and it shows some error:
Uncaught TypeError: setOpen is not a function from Child Component
Here is My Parent Component
<TableCell>
<Button
variant="contained"
size="small"
color="primary"
onClick={() => deleteHandler(index)}
>
Delete Me
</Button>
</TableCell>
{console.log(open)}
{open && <AddList open={open} setOpen={open} />}
My Child Component
export default function TransitionsModal(open, setOpen) {
const classes = useStyles();
// const [openL, setOpenL] = React.useState(null);
// const handleOpen = () => {
// setOpen(true);
// };
const handleClose = () => {
setOpen(!open);
};
return (
<div>
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
className={classes.modal}
open={open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500
}}
>
<Fade in={open}>
<div className={classes.paper}>
<h2 id="transition-modal-title">Transition modal</h2>
<p id="transition-modal-description">
react-transition-group animates me.
</p>
</div>
</Fade>
</Modal>
</div>
);
}
Your first issue is that you are passing a Boolean for the setOpen prop rather than the setOpen function itself, so change it to setOpen={setOpen}.
// RenderList.js
const RenderList = props => {
// ...
return (
...
{open && <AddList open={open} setOpen={setOpen} />}
)
}
Your second issue is that you're not destructing props properly in the TransitionsModal component. Use {} to destruct the props object and grab what you need.
// AddList.js
export default function TransitionsModal({ open, setOpen }) {
// ...
}
Here's the fixed example:
CodeSandbox
Hope this helps.
Hi take a look at this
https://codesandbox.io/s/frosty-bird-5yh5g
in RenderList.js you didn't pass setOpen
{open && <AddList open={open} setOpen={setOpen} />}
also export default function TransitionsModal({ open, setOpen }) {

Passing children in react

this is my first time to develop a react application.
I wanted to display 5 buttons in a table. The first approach I did is to hardcode it in Table.js
Table.js
<Button hidden={controlButton} onClick={() => reprocessConfirmation()}>
Reprocess
</Button>{" "}
<Button hidden={controlButton} onClick={() => reprocessConfirmation()}>
View Details
</Button>
<Button hidden={!controlButton} onClick={() => updateConfirmation()}>
Update
</Button>{" "}
<Button hidden={!controlButton} onClick={() => deleteConfirmation()}>
Delete
</Button>{" "}
<Button hidden={!controlButton} onClick={() => resequenceConfirmation()}>
Resequence
</Button>
But Table.js is designed to be reusable, so I was tasked to move these to index.js. I tried it by creating a var that has the function containing the buttons and return it to Table.js and display it using children props using this line {this.children}
index.js
const testButton = () => {
return (
<React.Fragment>
<button>Test1</button>
<button>Test2</button>
<button>Test3</button>
<button>Test4</button>
<button>Test5</button>
</React.Fragment>
);
};
return (
<React.Fragment>
<MyTable
{...{
testButton
}}
/>
</React.Fragment>
);
}
In Table.js I added
table.js
export default function MyTable({
testButton,
...props
}) {
return (
<React.Fragment>
<Table {...getTableProps()}>
*****{props.children}
</Table>
</React.Fragment>
It seems that when I use
this.children= it is undefined
props.children= does not display anything
Thank you very much.
It seems like you didn't pass pageOptions prop to MyTable component as you render children based on it's length.

Categories

Resources