I have a storybook project and created a new custom component. I used a hover state and when I hover the component, it updates its className and it just works fine. Named: ProductSize
And then, I created a new component to group the ProductSize component and named it as ProductSizeGroup and grouped them by the Json inside the ProductSizeGroup stories.
And here is the final product screen:
Here, I want to see the sizes when I hover the boxes. But, it shows me all the sizes all alone like this. Apparently, I only want to see XSmall when I hover to XS, Small in S etc..:
Edit: Many people asked for the coding side and that is here - a live coding example:
https://codesandbox.io/s/usestateissue-10l4l
So, how to solve it?
Here is the ProductSizeGroup component code displaying the ProductSize items and hover-triggered sizes
const ProductSizeGroup: React.FC<IProductSizeGroupProps> = (props) => {
const { ProductSizes } = props;
const [inHover, setHover] = useState(false);
return (
<Box style={{ width: "100%" }}>
<Typography>
{" "}
Size:
{ProductSizes.map((products: any) =>
inHover ? products.name : undefined
)}
</Typography>
<Box display="flex" justifyContent="flex-start" p={1} m={1}>
{ProductSizes.map((products: any) => (
<Box
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
>
<ProductSize
inStock={products.inStock}
sizeText={products.sizeText}
name={products.name}
/>
</Box>
))}
</Box>
</Box>
);
};
The issue is that you're displaying the size via the following
Size:
{ProductSizes.map((products: any) =>
inHover ? products.name : undefined
)}
where inHover is simply a Boolean value. So this will either show all name values or nothing.
I think what would work better is something like the following where you set the hovered state to the value you want and simply display it
const [hovered, setHovered] = useState<string | undefined>();
return (
<!-- snip -->
<Typography>Size: {hovered}</Typography>
<!-- snip -->
{ProductSizes.map(product => (
<Box
onMouseEnter={() => setHovered(product.name)}
onMouseLeave={() => setHovered(undefined)}
>
<!-- etc -->
</Box>
))}
)
Take note that I've also removed some of your any typings in the sandbox.
In order to handle states, you can use the react context provider: https://reactjs.org/docs/context.html or react redux https://react-redux.js.org/.
Depending on how many states you have and what you want to do with them you can choose one of the two. React-redux loads only the states you need, it is faster, but it has a specific structure that you need to follow. It is more complex at the beginning, but you can easier handle multiple states.
Context provider is already installed with react, it is easier to set up, but it is slower since it loads all states on each page load.
Related
I am new to React, and am trying to call a function which acts as a component, but returns two values, one called 'render' which just returns a component wrapped in a div, and another called sliderVal, which is an internal state value of that slider. Below is code from MySlider.js
function MySlider(props) {
const [sliderVal, setSliderVal] = useState(1);
return{
render:(
<div>
<Grid container justify = "center">
<Box sx={{ width: 250 }}>
<Typography id="input-slider" gutterBottom>
{props}
</Typography>
<Slider
defaultValue={1}
valueLabelDisplay="auto"
step={1}
value={sliderVal}
marks
min={1}
max={10}
onChange={(_, newValue) => setSliderVal(newValue)}
/>
</Box>
</Grid>
</div>
),
sliderVal
}
In my App.js, I am using the following code to render two sliders, and pass their values into another component.
var {render, sliderVal} = MySlider("Number of Paragraphs");
var {render1, sliderVal1} = MySlider("Number of Words");
The first one works just fine, I can use {render} to render the slider, and {sliderVal} to access its value and pass into another component. However the 2nd one does not work and nothing renders. When I console.log render1 and sliderVal1, they are both undefined. Any insight is greatly appreciated!
The 2nd change to:
var {render:render1, sliderVal:sliderVal1} = MySlider("Number of Words");
Right now I have a flatlist that contains a bunch of firebase objects (books), when one of the book are clicked, I want to return a page with more data about that specific book. Right now each book it an object, where title is one of the values, this is what is shown on the flatlist. I want to be able to show all of the other object attributes when the new detailed page is opened. If there is a better way of doing this, let me know but this is the logic that I was trying to go with.
(this is in the flatlist)
renderItem={({ item }) => (
<TouchableOpacity onPress={bookOnPressHandler} activeOpacity={0.9} style={styles.flatListStyle}>
<View>
<Text>{item.title}</Text>
</View>
</TouchableOpacity>
)}
/>
then the handler:
const bookOnPressHandler = (item) => {
//this holds the title of the book
title = item.title
console.log(title)
navigation.navigate('booknotes')
}
I obviously need to pass item into the function, what it is right now wont work. How would I get access to "Item" in the function? once I set the item = to something, I can use it on the new page.
I feel like there is a better method than this and that this might even not work? I know this is a common thing to do in apps, all help is really appreciated. Also sorry if its obvious, Im pretty new to this language and framework!
Since you already have access to the required object that is to be passed to the next screen, it's very easy, you can pass it along with the navigation object as a route param.
It's always good to isolate the prop functions to separate functions to avoid unnecessary re-render of the component.
Here is an example.
<FlatList
ref={ref}
contentContainerStyle={styles.contentContainer}
scrollEventThrottle={16}
numColumns={2}
data={exploreData}
initialNumToRender={2}
renderItem={renderItem}
keyExtractor={(item: any) => item.id.toString()}
})}
/>
Here is the renderItem function:
const renderItem = ({ item }) => {
const handleOnPress = () => navigation.navigate("Profile", { item });
return (
<TouchableWithoutFeedback
onPress={handleOnPress}
>
<ImageBackground
source={{ uri: img }}
style={styles.image}
imageStyle={styles.background}
/>
</TouchableWithoutFeedback>
);
};
//Profile Screen
const Profile = ({ navigation, route }) => {
const { item } = route.params;
console.log(item);
};
I am new to React and am building a tab component using Material UI's Tabs component. I'd like to place the Material UI badge component within the Tab component's label prop, but I'm not sure how to go about this.
The Tab component looks as such:
<Tab
key={i}
label={label}
{...globalTabProps}
{...tabProps}
classes={{
wrapper: cx('MuiTab-wrapper'),
}}
/>
I'm trying to add the badge as such:
const label = {
<Badge
color="primary"
className={
badgeProps.badgeContent === ''
? classNames(classes.MuiBadge, classes.MuiBadgeDotted)
: classNames(classes.MuiBadge, classes.MuiBadgeNumber)
}
badgeContent={''}
invisible={false}
{...globalBadgeProps}
{...badgeProps}
></Badge>
};
Of course, this errors out (parsing error), but I don't think this is the correct way to handle this anyway.
Would anyone be able to point me in the right direction?
Many thanks!
You should wrap it with (), like so.
const label = (
<Badge
color="primary"
className={
badgeProps.badgeContent === ''
? classNames(classes.MuiBadge, classes.MuiBadgeDotted)
: classNames(classes.MuiBadge, classes.MuiBadgeNumber)
}
badgeContent={''}
invisible={false}
{...globalBadgeProps}
{...badgeProps}
></Badge>
)
Note the () wrapping it.
Then do it like so:
<Tab
key={i}
label={label}
{...globalTabProps}
{...tabProps}
classes={{
wrapper: cx('MuiTab-wrapper'),
}}
/>
What it is done inside:
const WhateverComponent = (props) => (
<div>
...
{props.label}
</div>
);
I am a beginner, trying to understand react-popper. On their github, this is the example code:
import { Manager, Reference, Popper } from 'react-popper';
const Example = () => (
<Manager>
<Reference>
{({ ref }) => (
<button type="button" ref={ref}>
Reference element
</button>
)}
</Reference>
<Popper placement="right">
{({ ref, style, placement, arrowProps }) => (
<div ref={ref} style={style} data-placement={placement}>
Popper element
<div ref={arrowProps.ref} style={arrowProps.style} />
</div>
)}
</Popper>
</Manager>
);
Is this as it stands there supposed to be working? I am not sure I understand how I could use this code, how it would look like if I were to import/use this Example Component into my own parent component; Would I need to pass refs? How so? It would be great if somebody could give me a working example of this code, I'd just like to see how to make use of this.
I am guessing that you aim for a tooltip-like functionality. Is it what you are trying to achieve ?
Bear in mind that react-popper has a low level API. It is a positional engine rather than a tooltip library. By using it you would have to implement the logic of displaying the Popper yourself. For instance you could have a variable shouldShowElement and trigger changes to this
...
<Manager>
<Reference>
{({ ref }) => (
<button
type="button"
ref={ref}
onMouseEnter={() => {
/* [[SOME CODE TO SET shouldShowElement TO TRUE]] */
}}
onMouseLeave={() => {
/* [[SOME CODE TO SET shouldShowElement TO FALSE]] */
}}
>
Reference element
</button>
)}
</Reference>
{shouldShowElement ? (
<Popper placement="right">
{({ ref, style, placement, arrowProps }) => (
<div ref={ref} style={style} data-placement={placement}>
Popper element
<div ref={arrowProps.ref} style={arrowProps.style} />
</div>
)}
</Popper>
) : null}
</Manager>
...
If you want a more high-level API you can use react-tippy
or react-popper-tooltip
Don't bother too much with the render props. In most of the cases, you won't need to modify them.
Just keep in mind that
ref render prop from Reference should be attached to the
reference element (your item where is attached your popper).
ref render prop from Popper should be attached on the popper
(your tooltip or whatever).
style and placement render prop is for the geographical position on the page.
In resume, this example will render a button with an arrowed popperized <div> which contains "Popper element".
I wonder how I can access my props item in my doorsItem component. My code in the parent is this:
const doors = this.props.doors.data.map(item => <DoorsItem item={item} />)
My DoorsItem component looked like this before:
const DoorsItem = ({ item, customer }) =>
<Grid>
<Row key={item._id}>
<Col style={{ width: '100%' }}>
<ul className="door-list">
<li className="door-flex-container">
<div className="door-flex-item-1">
<h4 className="title-text-container">
{item.address.street} // Can use 'item' here
But I wanted to connect it with redux so I ended up with this:
class DoorsItem extends Component {
render() {
return (
<Grid>
<Row>
<Col style={{ width: '100%' }}>
<ul className="door-list">
<li className="door-flex-container">
<div className="door-flex-item-1">
<h4 className="title-text-container">
{/* How can I use it here? */}
</h4>
</div>
So I was wondering what’s the best way to access the item props in my new coded component?
Thanks for reading and sorry for the nooby question!
When you're doing <DoorsItem item={item} />, you're assigning the item prop, which means that within this component you can use it like this.props.item or even better const { item } = this.props; and use the item local variable later. So for example:
const { item } = this.props;
return <span>this is the {item} you're looking for</span>
More information about that on the official documentation.
Usually, when using class based components, we should define the propTypes and defaultProps. This gives us a better understanding of the class-based component's props to use and provides validation for props to be inserted. More information here (warning: moved to a separate package since React v15.5).
Finally, you can use context, which is not the recommended way but sometimes can be helpful.