I was looking for a way to know which list item is visible on the screen when using react-window .
The isVisible prop is returning the visibility wrongly .
https://codesandbox.io/s/bvaughnreact-window-fixed-size-list-vertical-nmukq
import React from "react";
import { render } from "react-dom";
import { FixedSizeList as List } from "react-window";
import AutoSizer from "react-virtualized-auto-sizer";
import TrackVisibility from "react-on-screen";
import "./styles.css";
const Row = ({ index, style, isVisible }) => (
<div className={index % 2 ? "ListItemOdd" : "ListItemEven"} style={style}>
Row {index + " is" + isVisible}
</div>
);
const RowWrapper = ({ index, style }) => (
<TrackVisibility>
<Row index={index} style={style} />
</TrackVisibility>
);
const Example = () => (
<AutoSizer>
{({ height, width }) => (
<List
className="List"
height={height}
itemCount={1000}
itemSize={35}
width={width}
>
{RowWrapper}
</List>
)}
</AutoSizer>
);
render(<Example />, document.getElementById("root"));
This could possibly be because of caching of items, but i was wondering if there is another way to track visibility of an item
I figured it out on my own . Please find the code sandboxes at
DynamicSizeList - https://codesandbox.io/s/piecykreact-window-dynamic-size-list-vertical-pooy2
By trapping the onWheel event and comparing the "tops" with clientHeight and ListHeight
FixedSizeList -https://codesandbox.io/s/bvaughnreact-window-fixed-size-list-vertical-nmukq
Also based on onWheel event.
It doesnt work with react-on-screen .
If someOne knows a better way of doing then please answer.
Related
import { Text, StyleSheet, View, Pressable } from 'react-native';
import { useState } from 'react';
import gridd from '../wordpuzzle.json'; // imports a 4x4 matrix
const WordsGrid = (props) => {
const handlePressed = (e) => {
console.log(e.target.style) // why it shows undefined even if I have set it below?
e.target.style.backgroundColor='black' // does nothing
}
return (
<View >
<>
{gridd.square.map(row =>
<View>
{row.map(letter => (
<>
<Pressable style={{backgroundColor:'red'}} onPress={(e) => handlePressed(e)}>
<Text >
{letter}
</Text>
</Pressable>
</>
))}
</View>)}
</>
</View>
);
}
export default WordsGrid;
Why e.target.style shows undefined, even if I have set the style below?
How to change a single (since I am putting there a little bit of them, because of the double for loop) Pressable item when pressed?
There are two issues in your code:
onPress callback's parameter is of type PressEvent and does not have e.target.style.
Even if it does have it, manipulating the style, which is the DOM element's attribute directly in React is not a good idea. (I know it's React Native but it's React too.)
Proper way of doing it is to add a state that changes upon pressing the Pressable, which in turn updates the style of the element through a classname.
As you have an array of Pressables, your state also needs to be an array-ish variable, I suggest an object with a unique key of row and column indexes. Pseudo-code:
const [readState, setReadState] = useState({});
// ...
handlePress = (index) => {
const newReadState = { ...readState };
newReadState[index] = true;
setReadState(newReadState);
}
// ...
{gridd.square.map((row, rowIndex) =>
<View>
{row.map((letter, colIndex) => (
<Pressable className={readState[`${rowIndex}${colIndex}`] ? 'read' : 'unread'} onPress={() => handlePress(`${rowIndex}${colIndex}`)}>
//...
// define styles for "read" and "unread" classnames here
export default function RenderPages({storage, setStorage, state, setState}){
const elRefs=[]
for(let i=0; i<storage[state.currentFolderId][state.currentFileId].content.length; i++){
elRefs.push(useRef())
}
return (
<>
{
renderable
?<div className="writing">
{storage[state.currentFolderId][state.currentFileId].content.map((page, index)=>
<div className='textarea'>
<textarea ref={elRefs[index]} placeholder='write here' value={page} id={"page"+index} onChange={(e)=>onChange(e, index)} rows={rows} cols={cols}></textarea>
</div>)}
</div>
: <></>
}
</>
)
}
I want to attach multiple ref to random number of "textarea" element. the number of element would be determined by the variable, "storage", which is given as props. I got error with above code. Help me please.
you don't need to use for loop to push the elements in ref, you already use map in return you can push textarea elements using ref like this way as you can see the below code, I hope this works. thanks
import { useRef } from "react";
export default function RenderPages({storage, setStorage, state, setState}) {
const elRefs = useRef([]);
return (
<>
{renderable ? (
<div className="writing">
{storage[state.currentFolderId][state.currentFileId].content.map((page, index) => (
<div className="textarea">
<textarea
ref={ref => {
elRefs.current[index] = ref
}}
placeholder="write here"
value={page}
id={'page' + index}
onChange={e => onChange(e, index)}
rows={rows}
cols={cols}></textarea>
</div>
))}
</div>
) : (
<></>
)}
</>
);
}
const myRefs = useRef([])
ref={ref => myRefs.current[index] = ref} // <--- Right syntax
Using one ref with multiple current elements is enough
Shouldn't (and don't need) call hook in a loop, in this case, it's invalid https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
I would like to wrap radio button group with 200px width. And I expect keyboard navigation to behave the same, which is to move between radio buttons through right/left arrow back and forth. Weird thing is if I implement parent div by wrapping this Container component const Container = ({ children }) => ( <div style={{ width: "200px" }}>{children}</div>);, after moving to different radio button once, it stops. I can't no longer navigate radio buttons. But if I implement parent div using plain html code <div style={{ width: "200px"}}>, keyboard navigation works fine. When I inspect using dev tools, semantically, they both appear the same. So I'm having trouble figuring out why Container component impacts keyboard navigation while hard coding in html doesn't. Anybody knows why? https://codesandbox.io/s/radio-button-example-3t8l80?file=/App.js
import { Stack, RadioButton } from "#shopify/polaris";
import { useState, useCallback } from "react";
function RadioButtonExample() {
const [value, setValue] = useState("disabled");
const handleChange = useCallback(
(_checked, newValue) => setValue(newValue),
[]
);
const Container = ({ children }) => (
<div style={{ width: "200px" }}>{children}</div>
);
return (
<Container> // keyboard navigation doesn't work properly with this
{/* <div style={{ width: "200px"}}> */} // keyboard navigation works fine
<Stack vertical>
<RadioButton
label="Accounts are disabled"
helpText="Customers will only be able to check out as guests."
checked={value === "disabled"}
id="disabled"
name="accounts"
onChange={handleChange}
/>
<RadioButton
label="Accounts are optional"
helpText="Customers will be able to check out with a customer account or as a guest."
id="optional"
name="accounts"
checked={value === "optional"}
onChange={handleChange}
/>
</Stack>
{/* </div> */}
</Container>
);
}
export default RadioButtonExample;
The reason for this to happen is because your Container component is part of the RadioButtonExample component. Every time a state or prop gets updated, the Container component gets created as a new component. To prevent this, take your Container component outside of the RadioButtonExample like this:
import { Stack, RadioButton } from "#shopify/polaris";
import { useState, useCallback } from "react";
const Container = ({ children }) => (
<div style={{ width: "200px" }}>{children}</div>
);
function RadioButtonExample() {
const [value, setValue] = useState("disabled");
const handleChange = useCallback(
(_checked, newValue) => setValue(newValue),
[]
);
return (
<Container>
<Stack vertical>
<RadioButton
label="Accounts are disabled"
helpText="Customers will only be able to check out as guests."
checked={value === "disabled"}
id="disabled"
name="accounts"
onChange={handleChange}
/>
<RadioButton
label="Accounts are optional"
helpText="Customers will be able to check out with a customer account or as a guest."
id="optional"
name="accounts"
checked={value === "optional"}
onChange={handleChange}
/>
</Stack>
</Container>
);
}
export default RadioButtonExample;
A different solution would be to wrap the container in a useMemo to make sure it stays the same. This would give you something like this:
const Container = useMemo(() => ({ children }) => (
<div style={{ width: "200px" }}>{children}</div>
), []);
Both will work, but I would suggest going for the first solution.
I am trying to build a reusable accordion, i was able to create an accordion with one level, but here i am stuck to have the nested accordion.
What i have tried so far
App.js
import "./styles.css";
import Accordion from "./Accordion";
import LIST from './Constants';
const listMaker = (item) => {
let faqItem;
if (item.children.length === 0) {
faqItem = (
<>
<Accordion title={item.name}></Accordion> <hr />
</>
);
} else {
let faqItemChildren = item.children.map((item) => {
let faqItem = listMaker(item);
return (
<>
${faqItem}
<hr />
</>
);
});
faqItem = <Accordion title={item.name}>{faqItemChildren}</Accordion>;
}
return faqItem;
};
let listItems = LIST.map((item) => {
let menuItem = listMaker(item);
return menuItem;
});
export default function App() {
return listItems;
}
have added codesandbox
I am new tor react, Any help is appreciated
Instead of using dangerouslySetInnerHTML you can use the children, as you need is a spread of React.ReactChildren. That would be just calling the {children} from props instead of the dangerouslySetInnerHTML
<div className="accordion__section">
<button className={`accordion ${setActive}`} onClick={toggleAccordion}>
<p className="accordion__title">{title}</p>
<Chevron className={`${setRotate}`} width={10} fill={"#777"} />
</button>
<div
ref={content}
style={{ maxHeight: `${setHeight}` }}
className="accordion__content"
>
{children}
</div>
</div>
Here is a forked solution of your codesandbox.
Also, Instead of setting the DOM to a variable, as its a conditional scenario, you can use the ternary operator, which helps in better readability.
const listMaker = (item) => {
return (
<>
{item.children.length === 0 ? (
<Accordion title={item.name} />
) : (
<Accordion title={item.name}>
{item.children.map((childItem) => {
return listMaker(childItem);
})}
</Accordion>
)}
</>
);
};
dangerouslySetInnerHTML is to use with strings. You shouldn't give an array of components to it. Yet you don't send any prop called content anyway. I think you meant children prop there. Just render children instead of using dangerouslySetInnerHTML
In your Accordion component replace this:
<div
className="accordion__text"
dangerouslySetInnerHTML={{ __html: props.content }}
/>
With this:
<div className="accordion__text">
{ props.children }
</div>
Created codesandbox to show issue:
https://codesandbox.io/s/agitated-https-2xjs2?fontsize=14&hidenavigation=1&theme=dark
I'm wanting a Tooltip to show whenever I'm hovering over a card. I need to pass in a value from the OverlayTrigger to the Tooltip component. With the following code, nothing is showing when hovering over a card:
Character.js:
import React from 'react'
import { Card, OverlayTrigger } from 'react-bootstrap'
import Infott from '../components/Infott'
const Character = ({ character }) => {
return (
<OverlayTrigger
trigger='hover'
placement='bottom'
overlay={<Infott test={'Test'} />}
>
<Card className='my-3 py-3 rounded'>
<a href={`/character/${character._id}`}>
<Card.Img src={character.image} />
</a>
</Card>
</OverlayTrigger>
)
}
export default Character
Infott.js:
import React from 'react'
import { Tooltip } from 'react-bootstrap'
const Infott = ({ test }) => {
return (
<Tooltip id='character-tooltip' placement='bottom'>
<strong>{test}</strong>
</Tooltip>
)
}
export default Infott
If I add className=show to the Tooltip component, it will show and the test value is passed, but it's no longer placed next to the card but rather the bottom left of the webpage. My guess is the OverlayTrigger and Tooltip are not on the same page.
I can get the Tooltip placement showing and in the correct placement if I change overlay to overlay={Infott} and then change my Tooltip component to
const Infott = (props) => {
return (
<Tooltip id='character-tooltip' placement='bottom' {...props}>
<strong>{test}</strong>
</Tooltip>
)
}
But then I'm not able to pass the test value that I need.
OverlayTrigger seems to use ref to the target Tooltip for some actions as well as some properties that it injects so in order to work properly you must forward a ref from your custom component to the target Tooltip you have wrapped inside.
So the correct solution for having your Tooltip wrapped in a custom component should be something like:
const Infott = React.forwardRef(({test, ...props}, ref) => {
return (
<Tooltip id='character-tooltip' ref={ref} placement='bottom' {...props}>
<strong>{test}</strong>
</Tooltip>
);
});