Is this possible to dynamically increase table height, based on the browser window height, when using the virtualized react-table (example that I'm working with) ?
There's lots of empty space in my browser that I wish it'll fill up, yet I don't wish the table to overflow my screen, only to go up to the end of it (with a scrollbar allowing access to the remaining rows, if any).
I tried to use this flex box css code, but it didn't do anything. Note that I've successfully used this css code for a different component and it worked.
.flexbox-container {
display: flex;
height: 100vh;
flex-direction: column;
}
.test1 {
display: flex;
flex-grow: 1;
}
I can see that the react-table example relies on FixedSizeList which seems to fix the height, which perhaps is disabling the flex box? I've tried to remove all the optional props but it didn't work.
<FixedSizeList
height={800}
itemCount={rows.length}
itemSize={35}
width={totalColumnsWidth+scrollBarSize}
>
{RenderRow}
</FixedSizeList>
When my application have multiple datas i solve with pagination
After user have a pagination hell (many-page)
i used react-window to solve problem after i confroted to your probleme
is solved to this code this sell hooks
import React from 'react'
/**
* Get small piece of screen to dynamic height table
*/
const calcHeight = (val) => (val - (Math.round(val / 4)))
export default function useHeight() {
const [height, setHeight] = React.useState(calcHeight(window.innerHeight))
const detectWindowHeight = (h) => setHeight(calcHeight(h))
React.useEffect(() => {
window.addEventListener("resize", (e) => detectWindowHeight(e.target.innerHeight))
return () => window.removeEventListener("resize", (e) => detectWindowHeight(e.target.innerHeight))
}, [])
return { wH: height }
}
you can use variable wH => to dynamic height your table
Thanks to respond me !!!
Related
I am using Mui Grid and want to generate fixed size grid elements to fill the screen (both height and width) but I don't know how to do that.
Here is a CodeSandbox link of something basic I am working with. In this example, I hard coded it to render 20 elements. I instead want it to account for the screen size, padding, margin, etc. to programmatically determine how many elements to render. Is this possible?
I'm not sure it's possible using Grid, but with plain JavaScript, you should be able to calculate the number of elements to render to fill a page and an event listener on "resize" to continually calculate the proper number of elements to render to completely fill the page width.
CodeSandbox Example
const itemWidth = 100;
const gridGap = 10;
const Item = styled(Paper)(({ theme }) => ({
textAlign: "center",
width: `${itemWidth}px`,
height: "100px"
}));
export default function ResponsiveGrid() {
const [numOfEl, setNumOfEl] = useState(
Math.floor(window.innerWidth / (itemWidth + gridGap))
);
function getNumberOfElements() {
setNumOfEl(Math.floor(window.innerWidth / (itemWidth + gridGap)));
}
useEffect(() => {
window.addEventListener("resize", getNumberOfElements);
return () => window.removeEventListener("resize", getNumberOfElements);
}, []);
return (
<Grid container sx={{ gap: `${gridGap}px` }}>
{Array.from(Array(numOfEl)).map((_, index) => (
<Grid key={index}>
<Item>{index}</Item>
</Grid>
))}
</Grid>
);
}
Edit: I only calculated the number for the width, but you get the idea.
I have some repeated hover states that runs a function to display some empty or filled icons (like you would see in some e-commerce websites with empty/filled carts). As practice, I wanted to create and put this into a custom hoverHooks component, w/ useRef and useEffect to run some add/remove eventListeners, likeso:
const ref = useRef(null)
function enter() {
setHover(true)
}
function leave() {
setHover(false)
}
useEffect(() => {
ref.current.addEventListener('mouseenter',enter)
ref.current.addEventListener('mouseleave', leave)
return () => {
ref.current.removeEventListener('mouseenter',enter)
ref.current.removeEventListener('mouseleave',leave)
}
})
I did this so that the container holding my icons can just have the ref={ref} without me having to repeatedly write onMouseEnter / onMouseLeave. (I guess my refs are being repeated, but better three letters, and move my hover state to just one place.
Cannot read property 'removeEventListener' of null, is what I get. I read the React 17 docs regarding this, under "potential issues." But their suggestion isn't working (capturing the mutable data by storing it into a variable).
useEffect(() => {
const myRef = ref.current
myRef.current.addEventListener('mouseenter',enter)
myRef.current.addEventListener('mouseleave', leave)
return () => {
myRef.current.removeEventListener('mouseenter',enter)
myRef.current.removeEventListener('mouseleave',leave)
}
})
Any and all advice would be greatly appreciated!
Thank you
Is that what you are looking for?
/* HOVER HOOK */
const useHover = ({ ref, onMouseEnter, onMouseLeave }) => {
React.useEffect(() => {
if (ref.current) {
ref.current.addEventListener('mouseenter',onMouseEnter);
ref.current.addEventListener('mouseleave',onMouseLeave);
}
return () => {
ref.current.removeEventListener('mouseenter',onMouseEnter);
ref.current.removeEventListener('mouseleave',onMouseLeave);
};
},[ref,onMouseEnter,onMouseLeave]);
return;
};
/* APP */
function App() {
const ref = React.useRef(null);
const onMouseEnter = () => console.log("ENTER");
const onMouseLeave = () => console.log("LEAVE");
useHover({ref,onMouseEnter,onMouseLeave});
return(
<div className="app" ref={ref}>
Hover me
</div>
);
}
ReactDOM.render(<App/>, document.getElementById("root"));
.app {
width: 100px;
height: 100px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
There is solution from YouTube:
const refHandle = useRef();
<i ref={refHandle}>
// This is to revise the original
refHandle.current.removeEventListener('touchstart', onStartTouchBegin, false);
// To the conditional format,
(ref.current)?.removeEventListener('touchstart', onStartTouchBegin, false);
I came across this recently in a test using jest and #testing-library/react. I was surprised none of automatic cleanup functions were handling this so I did some more digging and found an old git-issue which lead to this post in the react documentation (note, this is in reference to React v17.0 Release Candidate). To quote:
The problem is that someRef.current is mutable, so by the time the cleanup function runs, it may have been set to null. The solution is to capture any mutable values inside the effect:
useEffect(() => {
const instance = someRef.current;
instance.someSetupMethod();
return () => {
instance.someCleanupMethod();
};
});
Again, this was in regards to v17.0 release candidate so I don't suspect this to be a problem in later versions. I am using React v17.0.2 which leads me to believe this problem stems from our code and/or how we are using react-hooks with third-party libs.
I figured I'd share this information anyways in case someone runs across this problem and is looking for a solution that avoids memory leaks from not cleaning stranded event listeners. Cheers
Instead of using ref.current in the cleanup function:
useEffect(() => {
someRef.current.someSetupMethod();
return () => {
someRef.current.someCleanupMethod();
};
});
Capture the ref inside the effect.
useEffect(() => {
const instance = someRef.current;
instance.someSetupMethod();
return () => {
instance.someCleanupMethod();
};
});
Checkout the documentation for more details:
The problem is that someRef.current is mutable, so by the time the
cleanup function runs, it may have been set to null. The solution is
to capture any mutable values inside the effect:
I have multiple(more than 15) div tags as tiles. I need to emphasis each one if mouse hover on it. So each tag has onMouseEnter/Leave functions as bellow.
<div
key={key}
onMouseEnter={onMouseEnter(key)}
onMouseLeave={onMouseLeave(key)}
>
...
</div>
Also I put each tiles key in a Map data structure.
const onMouseEnter = key => {
return function() {
const newIsHover = new Map(isHover)
newIsHover.set(key, true)
setIsHover(newIsHover)
}
}
const onMouseLeave = key => {
return function() {
const newIsHover = new Map(isHover)
newIsHover.delete(key)
setIsHover(newIsHover)
}
}
Since component is hook it put its state in a useState.
const [isHover, setIsHover] = useState(new Map())
What is happening here:
Always I enter a tile: onMouseEnter function called and its key added to map (as expected)
When I leave a tile: always onMouseLeave called but sometimes key is removed (as expected) and tile turned back to its normal shape but sometimes it does not(problem is here, in this situation map updated at setIsHover in onMouseLeave but it does not changed in the component!).
I think map updated as expected but when I move on new tile it does not understand that yet. So it overwrite it with what it has.
PS: example added. Move between tiles with high speed!
Like the class-based components, calls to update state are asynchronous and get queued up. Try using functional state updates to ensure these queued-up updates correctly update the previous state. This should fix race conditions between quick successive setIsHover calls with the same key.
Notice if you move slowly enough between tiles they correctly highlight and unhighlight, but more quickly (like a swipe) and 2 or more can get stuck until you again slowly exit the tile.
const onMouseEnter = key => {
return function() {
setIsHover(prevIsHover => {
const newIsHover = new Map(prevIsHover);
newIsHover.set(key, true);
return newIsHover;
});
}
}
const onMouseLeave = key => {
return function() {
setIsHover(prevIsHover => {
const newIsHover = new Map(prevIsHover);
newIsHover.delete(key);
return newIsHover;
});
}
}
But I should note that this is a lot of leg work for simply applying some component styling, especially hovering. It could more simply be achieved using CSS.
tileStyles.css
.tile {
background-color: lightgray;
border: 3px solid black;
height: 100px;
line-height: 100px;
margin: 10px;
text-align: center;
width: 100px;
}
.tile:hover {
border-color: red;
}
tile.jsx
import React from "react";
import { withStyles } from "#material-ui/core";
import "./tileStyles.css";
const styles = {
container: { display: "flex", width: "600px", flexWrap: "wrap" }
};
const Tiles = ({ classes: { container }, tiles }) => {
return (
<div className={container}>
{tiles.map((tl, key) => {
return (
<div className="tile" key={key} name={key}>
hi
</div>
);
})}
</div>
);
};
export default withStyles(styles)(Tiles);
The normal and hovered styles are applied together (at the same time) and CSS/html will manage when it hovered or not. The component no longer requires event listeners and doesn't need to maintain internal state.
Explanation
what means "...calls to update state are asynchronous and get queued up."?
When you call this.setState or a useState update function the update doesn't happen synchronously right then and there, but they are queued up during the current render cycle and batch processed in the order in which they were queued. Perhaps this demo will help illustrate what happens. What confounds this issue is the fact that event processing is also asynchronous, meaning that, when events occur their registered callbacks are placed in the event queue to be processed.
I am currently experiencing a structural problem with React and styled-components where I cannot find a seemingly nice solution for every case.
Here's the (quite simple) problem:
<Margin top="10%" left="5%">
<Card>Some text</Card>
</Margin>
The Margin component extracts the margin settings from the Card component so that the Card component can be reused in other scenarios. The Margin component itself can be reused aswell.
If one wanted to make this responsive though, it would only be possible via different props like topPhone, topTablet and so on, which I find very unintuitive and hard to maintain if the responsive boundaries ever change.
So what one could do is duplicate all the content into different other components and display only the one that currently fits (e.g. react-responsive's MediaQuery component).
But in that case, the page would have a lot of overhead for different device sizes.
What would be the best way to approach this?
Here’s an outline of the implementation I use (slimmed down), it may fit your case. It involves a little set up but helps keep responsive boundaries maintainable (which I believe is the main concern).
1) Setup an object with your desired responsive boundaries:
const screenSize = {
sm: 576,
md: 768,
lg: 992,
};
2) Create a Theme to help pass those boundaries through your application via styled-components. Here I set them up with the breakpoints (media-queries) in mind, using something like Bootstrap as a guideline (These should be extended to include smMin, mdMin etc):
const theme = () => {
return {
xsMax: screenSize.sm - 1 + 'px',
smMax: screenSize.md - 1 + 'px',
mdMax: screenSize.lg - 1 + 'px',
};
};
3) Wrap your application with your Theme as mention here.
render() {
return (
<ThemeProvider theme={theme}>
<MyApplication />
</ThemeProvider>
)
}
4) Using styled-components’ tips-and-tricks as an example. Create an exportable media helper to make use of those Theme breakpoints (I’ll only illustrate 2 breakpoints, but you should expand this to include xsDown, smDown, lgUp, mdOnly etc etc)
export const media = {
smDown: (...args) => css`
#media (max-width: ${({ theme }) => theme.smMax}) {
${css(...args)};
}
`,
mdDown: (...args) => css`
#media (max-width: ${({ theme }) => theme.mdMax}) {
${css(...args)};
}
`,
};
5) import media and use it like a normal old media-query wrapping your responsive styling:
const MyComponent = styled('section')`
/* Everything to be pink on screens with widths < 768px */
${media.smDown`
* {
background: papayawhip;
}
`};
`;
const MyOtherComponent = styled(‘li’)`
/* Everything to be purple on screens with widths < 992px */
${media.mdDown`
* {
background: purple;
}
`};
`;
You can import media and reused it in Margin or any other component in your application. Responsive boundaries are maintained in one place.
Have you checked out react-socks yet? It helps building responsive React components.
<Breakpoint topPhone only>
<div>I will render only in topPhone</div>
</Breakpoint>
<Breakpoint topTablet only>
<div>I will render only in topTablet</div>
</Breakpoint>
Disclaimer: I am the author of this library.
I'd like to position a component just above the createBottomTabNavigator TabBar in React-Navigation V2.
The height of the tab bar seems to differ on various devices (iOS devices especially). Is there a way to calculate the height of the tab bar as it is displayed on a device?
As you check the source code for react-navigation-tabs which react-navigation uses for createBottomTabNavigator, you can see that there is only 2 different bottom tab bar heights. Compact and default, which changes between some conditions. You can also set your component's position according to these conditions manually.
React Navigation 5 +
You now have two options to get the height of the bottomTabBar.
To get the height of the bottom tab bar, you can use BottomTabBarHeightContext with React's Context API or useBottomTabBarHeight, which is a custom Hook:
import { BottomTabBarHeightContext } from '#react-navigation/bottom-tabs';
// ...
<BottomTabBarHeightContext.Consumer>
{tabBarHeight => (
/* render something */
)}
</BottomTabBarHeightContext.Consumer>
or
import { useBottomTabBarHeight } from '#react-navigation/bottom-tabs';
// ...
const tabBarHeight = useBottomTabBarHeight();
Make sure you use version 5.11.9 or greater
To avoid Ipnone X issues they use react-native-safe-area-view inside.
You just need to know padding at bottom:
import { getInset } from 'react-native-safe-area-view'
const bottomOffset = getInset('bottom')
It solved problem for us.
We also use specific component position.
Updated according to library update:
import { SafeAreaConsumer } from 'react-native-safe-area-context'
<SafeAreaConsumer>
{insets => (
<TouchableOpacity
style={{
paddingBottom: 11 + insets.bottom,
}}
>
...
</TouchableOpacity>
)}
</SafeAreaConsumer>
or hook:
const insets = useSafeArea();
For your issue of how to position something above the tab bar, you can also achieve this without absolute positioning. This way you aren't relying on how the logic of determining the height of the bar is implemented (which may also change in the future).
import { createBottomTabNavigator, BottomTabBar } from "react-navigation"
createBottomTabNavigator({
// Your tabs
}, {
tabBarComponent: (props) => <BottomTabBar {...props} />
})
For example, if you wanted a little red bar above your tabs, you could do the following
tabBarComponent: (props) => (
<View>
<View style={{ backgroundColor: "red", height: 10 }} />
<BottomTabBar {...props} />
</View>
)
The other answer by benny points to where you need to go, but doesn't give you an easy way to check if . To complete the answer, I'll elaborate on the exact checks required to know which height to use. First we need to know if the tab bar is in adaptive mode or not. If you haven't passed "adaptive" as a parameter, adaptive is set to true for all iOS devices with iOS 11+. If it's not iOS11+, then adaptive is false. So, if you HAVE NOT passed "adaptive" as a parameter to tabBarOptions, the function is:
import {Platform, Dimensions} from 'react-native';
const isLandscape = () => {
const dim = Dimensions.get('screen');
return dim.width >= dim.height;
};
function tabBarHeight() {
const majorVersion = parseInt(Platform.Version, 10);
const isIos = Platform.OS === 'ios';
const isIOS11 = majorVersion >= 11 && isIos;
if(Platform.isPad) return 49;
if(isIOS11 && !isLandscape()) return 49;
return 29;
}