How to make background parallax in React? - reactjs

I am trying to make a parallax background like the react-parallax package.
I couldn't use react-parallax package it gives me 504 error (problem with vite).
I tried to use #react-spring/parallax but it didn't do the job.
Finally I wrote my own code but I still confused wether my code has a bad performace.
I've made a scroll listener with a callback which sets scroll state to scrollY value
then using this value with background position y
useEffect(() => {
window.addEventListener("scroll", () => setscroll(window.scrollY));
return () => {
window.removeEventListener("scroll", () =>
setscroll(window.scrollY)
);
};
}, []);
<Swiper>
<SwiperSlide
style={{
backgroundImage: `url(${Img[0]})`,
backgroundSize: "cover",
backgroundPositionX: "center",
backgroundPositionY: `${(scroll - 60) * 0.3}px`,
width: "100%",
backgroundRepeat: "no-repeat",
overflow: "hidden",
position: "relative",
}}></SwiperSlide>
</Swiper>
The performace issue I touched is this component renders every time I scroll.
What should I do?

Related

Making signaturePad component take up full screen

Within one of my components a user can click a button that pulls up a signaturePad they can sign on. This is working, however, I'd like the signaturePad to take up the full screen, whereas now it appears between the footer and header. How can I accomplish this? I played around with using { flex: 1 } on the View of the SignaturePad, but this had no visible effect.
Here is the relevant code from the parent component. If a certain state is true, the signaturePad displays:
{this.state.signaturePanelIsVisible && (
<SignaturePanel
session={this.props?.session}
signaturePanelIsVisible={this.state.signaturePanelIsVisible}
/>
)}
And the signaturePad component code looks like this:
const SignaturePanel = (props) => {
const dispatch = useDispatch();
return (
<SignaturePanel
actionStyle={{
...styles.texts.mediumText,
color: styles.colors.primary,
textDecorationLine: 'underline',
}}
onCancel={async () => {
props.hideSignaturePanel();
}}
onSave={async (base64) => {
const base64Result = base64.base64DataUrl.substr(base64.base64DataUrl.indexOf(',') + 1);
dispatch(
await updateSignature({
...props.signature,
guid: props.session.guid,
value: base64Result,
lastUpdate: Date.serverTime(),
})
);
}}
/>
);
};
export default SignaturePanel;
And the styling applied looks like this:
container: {
width: '100%',
height: '100%',
zIndex: 98,
position: 'absolute',
backgroundColor: '#fff',
},
You'll need to add position: 'absolute' to the style of your modal. Setting the height and width to 100% as well sometimes works, depending on how your app is set up. This will either cover the screen with the component, or make the component appear under the header and overflow the bottom of the screen. To fix the latter issue, add top: -<height of header>. This way the component will move up and cover the header.
<Modal style={styles.modal}>
<View style={styles.signaturePad}>
</View>
</Modal>
modal: {
position: 'absolute',
height: '100%',
width: '100%',
top: -n, //(where n = height of your header)
}
signaturePad: {
height: '100%',
width: '100%',
}
Depending on how your project is set up, you may not need the top value. These are general settings that you should usually put on a modal component. It's considered best practice to have a modal cover the whole screen, then add the component you want to display as a subcomponent.

React Testing Library: How to mock dom-ref properties without them being overwritten?

I am testing a react component that renders a container with overflow:hidden with children, which has an effect hook that calculates the visible elements.
I want to test that feature by mocking the clientWidth value of the container ref, but I can't get it to work. My mock of useRef gets called but my mocked value gets overwritten after the first render.
How do I mock the value of clientWidth or is there another way to test this behavior?
Sandbox
My component:
function OverflowingComponent() {
const divRef = React.useRef<HTMLDivElement>(null);
const [visibleChildren, setVisibleChildren] = React.useState(0);
React.useEffect(() => {
if (divRef.current) {
const containerChildren = divRef.current.children;
const maxVisibleChildren = Math.round(divRef.current.clientWidth / containerChildren[0].clientWidth);
setVisibleChildren(
containerChildren.length < maxVisibleChildren ? containerChildren.length : maxVisibleChildren
);
}
}, []);
return (
<>
<label>{visibleChildren} elements are visible!</label>
<div ref={divRef} style={{ display: 'flex', overflow: 'hidden' }}>
<div style={{ width: 50, flexShrink: 0 }}>foo</div>
<div style={{ width: 50, flexShrink: 0 }}>bar</div>
<div style={{ width: 50, flexShrink: 0 }}>boo</div>
<div style={{ width: 50, flexShrink: 0 }}>far</div>
</div>
</>
);
}
My test:
describe('OverflowingComponent', () => {
it('only 3 elements should be visible with a container with of 200px', () => {
jest.spyOn(React, 'useRef').mockImplementation(() => ({
current: {
clientWidth: 150
}
}));
render(<OverflowingComponent />);
expect(screen.getByText('3 elements are visible!')).toBeInTheDocument();
});
});
Since the ref-mock does not work the test always fails with:
Unable to find an element with the text: 3 elements are visible!. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
Ignored nodes: comments, script, style
NaN
elements are visible!

is it possible to animate a strikethrough with React Spring?

I'm new to React Spring and I initially tried this
const strikeProps = useSpring({
textDecoration: "line-through",
from: { textDecoration: "none" },
});
But it's not working. I think there should be a way simulate the CSS solution for this.
The problem here is, that the original CSS solution is uses pseudo element for emulating the strike trough. We can only add react-spring properties for normal html elements. So the most compact way is to create a separate strike through component for this problem. For example:
const StrikeTroughtText = ({ children, weight = 1 }) => {
const props = useSpring({
from: { width: "0%" },
to: { width: "100%" }
});
return (
<div style={{ position: "relative", display: "inline-block" }}>
{children}
<animated.div
style={{
position: "absolute",
top: "50%",
left: 0,
width: props.width,
height: `${weight}px`,
background: "black"
}}
/>
</div>
);
};
We basically animate the width of the absolutely positioned div containing a black line over the text.
You can use it like a div component:
<StrikeTroughtText>text</StrikeTroughtText>
For bigger font size the default 1 px line weight is not enough, so I added a weight property also.
Here is my example: https://codesandbox.io/s/react-strike-trought-text-component-with-react-spring-animation-86cfd?file=/src/App.js

Hide element when keyboard is shown

I want to hide my button when keyboard is shown and and move it back when User closes keyboard. It works but I've noticed really annyoing effect of jumping button, really ugly 0.0001 animation. I think that the problem exsists because my code hides element right after my component rerenders. Could you help me to avoid this type of "jumping"?
const [isKeyboardVisible, setKeyboardVisible] = React.useState();
useEffect(() => {
Keyboard.addListener('keyboardDidShow', function() {
setKeyboardVisible(false);
});
Keyboard.addListener('keyboardDidHide', function() {
setKeyboardVisible(true);
});
});
{isKeyboardVisible && (
<Button
style={{
position: 'absolute',
borderRadius: 0,
bottom: 0,
width: '100%',}}
title="Delete"
}}
/>
)}

How do you toggle/expand specific mapped button IN a component using react hooks?

Every time I expand a specific card, all character cards expand, when I only want the specific one to. I am using the star wars api, using react-hooks and material-ui.
From what I know and read, I am able to pass the index through the handleClick() event but in the handleClick function itself is where I get lost. I try to attach the index to the expanded state but I will get an error saying I can't attach the number to a boolean. I also understand that separating the component and mapping could be the solution and that makes sense but I haven't been able to do it. It's been a couple days so now im reaching out for help if possible. Thanks.
The app.js File
const [loading, setLoading] = useState(false);
const [data, setData] = useState([]);
useEffect(() => {
setLoading(true);
const fetchData = async () => {
try {
const result = await axios.get("https://swapi.co/api/people/");
setLoading(false);
// console.log(result.data.results);
setData(result.data.results);
} catch (error) {
console.log("there was an error");
}
};
fetchData();
}, []);
return (
<div className="App">
<CssBaseline />
<Typography variant="h1">React Wars</Typography>
<Icon>star</Icon>
<MediaCard data={data} />
</div>
);
};
export default App;
The card.js file
const useStyles = makeStyles(theme => ({
card: {
width: 200,
marginBottom: 16
},
media: {
height: 0,
paddingTop: "56.25%" // 16:9
},
expand: {
transform: "rotate(0deg)",
marginLeft: "auto",
transition: theme.transitions.create("transform", {
duration: theme.transitions.duration.shortest
})
},
expandOpen: {
transform: "rotate(180deg)"
},
avatar: {
backgroundColor: red[500],
margin: 0
},
card_container: {
display: "flex",
maxWidth: 1064,
flexWrap: "wrap",
margin: "auto",
justifyContent: "space-between"
}
}));
const classes = useStyles();
const [expanded, setExpanded] = React.useState(false);
function handleExpandClick(index) {
setExpanded(!expanded);
}
const a = props.data.map((data, index) => {
return (
<Card className={classes.card} key={index}>
<div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
marginTop: 16
}}
>
<Avatar aria-label="recipe" className={classes.avatar}>
{data.name.charAt(0)}
</Avatar>
{
<Typography variant="h5" key={index} style={{ margin: "8px auto" }}>
{data.name}
</Typography>
}
</div>
<IconButton
className={clsx(classes.expand, {
[classes.expandOpen]: expanded
})}
onClick={() => handleExpandClick(index)}
aria-expanded={expanded}
aria-label="show more"
>
<ExpandMoreIcon />
</IconButton>
<Collapse in={expanded} timeout="auto" unmountOnExit>
<CardContent>
<Typography paragraph>{data.gender}</Typography>
<Typography paragraph>{data.eye_color}</Typography>
<Typography paragraph>{data.height}</Typography>
</CardContent>
</Collapse>
</Card>
);
});
return <div className={classes.card_container}>{a}</div>;
};
export default MediaCard;
UPDATE:
I separated the components and am able to show only the data for each character but still not able to isolate the expand function to the specific card. I created a codesandbox link to see my code if that helps. Thanks.
https://codesandbox.io/embed/determined-frog-p0k8w?fontsize=14
Thanks for your help. I set up a codesandbox to see my code. Progress I have made so far is only displaying data of the specific character but still having difficulties understanding how to create the mapped data's own state. Thanks
https://codesandbox.io/embed/determined-frog-p0k8w?fontsize=14
UPDATE: I FIGURED IT OUT
The issue was styling as my code was right (not sure if structurally right as Im still learning but functional lol). The row the card was on increased the height of all cards in that row only so I controlled the height of each card. A quick fix for me was making the card height 100% and it seemed to work. I did do one without material-ui's expand still which did help me see the issue so thanks for the advice.
If you see anything else in my code that maybe Im doing wrong or couldve done better please let me know as I am always trying to improve. Ill attach both code samples below.
WITH MATERIAL-UI EXPAND
https://codesandbox.io/s/determined-frog-p0k8w?fontsize=14
WITHOUT Material-UI Expand
https://codesandbox.io/embed/great-burnell-ju158?fontsize=14

Resources