React - how to update state from the prop of other library - javascript

I am using react-scrollmagic for scrolling effect.
When I use its component, there is a prop called progress,
{(progress, event) => (
…
)}
Which I want to update my state currentProgress by.
How can I do it?
App.js
import "./styles.css";
import styled from "styled-components";
import { Controller, Scene } from "react-scrollmagic";
import React, { useState, useEffect } from "react";
const ClassToggleStyled = styled.div`
.section {
height: 100vh;
}
.test {
transition: width 0.3s ease-out;
width: 100px;
height: 100px;
background-color: red;
margin: 0 !important;
&.yellow {
background-color: yellow;
}
}
.zap {
width: 100%;
}
`;
export default function App() {
const [currentProgress, setCurrentProgress] = useState(0);
useEffect(() => {
// I want to update the currentProgress whenever the progress changed becausing of scrolling
setCurrentProgress(0);
}, []);
return (
<ClassToggleStyled>
<div style={{ position: "fixed", top: 0 }}>
Current Progress: {currentProgress}
</div>
<div className="section" />
<div id="trigger" />
<Controller>
<Scene
duration={200}
classToggle="zap"
triggerElement="#trigger"
indicators={true}
>
{(progress, event) => (
<div className="test">
<div style={{ position: "fixed", top: 30 }}>
Progress: {progress}
</div>
Pin Test {event.type} {progress}
</div>
)}
</Scene>
<Scene
classToggle={[".test", "yellow"]}
reverse={false}
indicators={true}
>
<div>Toggle other class</div>
</Scene>
</Controller>
<div className="section" />
</ClassToggleStyled>
);
}
Codesandbox
https://codesandbox.io/s/nifty-hermann-byrvz?file=/src/App.js
Update 1
I updated the source code based on comment.
And I added a button to update currentProgress but I find it not working.
Error in Console
Warning: unmountComponentAtNode(): The node you're attempting to unmount was rendered by another copy of React.
Warning: Cannot update a component (`App`) while rendering a different component (`SceneBase`). To locate the bad setState() call inside `SceneBase`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render
App.js
import "./styles.css";
import styled from "styled-components";
import { Controller, Scene } from "react-scrollmagic";
import React, { useState, useEffect } from "react";
const ClassToggleStyled = styled.div`
.section {
height: 100vh;
}
.test {
transition: width 0.3s ease-out;
width: 100px;
height: 100px;
background-color: red;
margin: 0 !important;
&.yellow {
background-color: yellow;
}
}
.zap {
width: 100%;
}
`;
export default function App() {
const [currentProgress, setCurrentProgress] = useState(0);
return (
<ClassToggleStyled>
<button
onClick={() => {
console.log("update progress to 0.5");
setCurrentProgress(0.5);
}}
style={{ position: "fixed", top: 50 }}
>
update currentProgress
</button>
<div style={{ position: "fixed", top: 0 }}>
Current Progress: {currentProgress}
</div>
<div className="section" />
<div id="trigger" />
<Controller>
<Scene
duration={200}
classToggle="zap"
triggerElement="#trigger"
indicators={true}
>
{(progress, event) => {
setCurrentProgress(progress);
return (
<div className="test">
<div style={{ position: "fixed", top: 30 }}>
Progress: {progress}
</div>
Pin Test {event.type} {progress}
</div>
);
}}
</Scene>
<Scene
classToggle={[".test", "yellow"]}
reverse={false}
indicators={true}
>
<div>Toggle other class</div>
</Scene>
</Controller>
<div className="section" />
</ClassToggleStyled>
);
}
Codesandbox
https://codesandbox.io/s/great-noether-dtfok?file=/src/App.js

You can set the state in the library's event handler:
{(progress, event) => {
setCurrentProgress(progress);
return (
<div className="test">
<div style={{ position: "fixed", top: 30 }}>
Progress: {progress}
</div>
Pin Test {event.type} {progress}
</div>
)}
}
Updated sandbox

Related

Inline Styling Individual React Components

I am trying to move individual components with inline styles as I want them in different places of the website. Why is this code not working?
import "./App.css";
import React from "react";
import Window from "./components/Window";
class App extends React.Component {
render() {
return (
<div className="App">
<Window
id="firstWindow"
style={{ position: "relative", left: "200px" }}
number={"1"}
/>
<Window id="secondWindow" number={"2"} />
<Window id="thirdWindow" number={"3"} />
</div>
);
}
}
export default App;
this code works in a different section of the app
import React from "react";
import "./Window.css";
class Window extends React.Component {
render() {
return (
<div
className="square"
style={{ position: "relative", left: "200px" }}
>
{this.props.number}
</div>
);
}
}
export default Window;
Inline style is passed by property name style, use it to set style in the component.
style={ this.props.style }
A demo:
class App extends React.Component {
render() {
return (
<div className="App">
<Window
id="firstWindow"
style={{ position: "relative", left: "200px" }}
number={"1"}
/>
<Window
id="secondWindow"
style={{ position: "relative", left: "100px" }}
number={"2"} />
<Window
id="thirdWindow"
style={{ position: "relative", left: "10px" }}
number={"3"} />
</div>
);
}
}
class Window extends React.Component {
render() {
return (
<div
className="square"
style={ this.props.style }
>
{this.props.number}
</div>
);
}
}
// ========================================
ReactDOM.render(
<App />,
document.getElementById('root')
);
.square {
background: #fff;
border: 1px solid #999;
float: left;
font-size: 24px;
font-weight: bold;
line-height: 34px;
height: 34px;
margin-right: -1px;
margin-top: -1px;
padding: 0;
text-align: center;
width: 34px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
So here's a code sandbox with a working example based on your code-
https://codesandbox.io/s/strange-borg-hd2o4?file=/src/App.js
First of all, you need to be using position: absolute if you're going to use left: 200px. When it's position: relative, there's no effect.
Secondly, a better way to style these is to use the ids you've already created and then pass it as a prop to the component, check out the example.
Thirdly, React class components are on the way out - you should be learning the React function approach first. I recommend https://fullstackopen.com/en/.
Hopefully that all makes sense!

CSSTransition react: Only backdrop shown and photo not being displayed

I have a component that gets rendered when a photo is clicked with CSSTransition. It also has a Backdrop component but upon click only the backdrop gets shown.
If I remove CSSTransition then it behaves as expected.
Here's the photo viewer component:
import styles from './PhotoZoomViewer.module.scss';
import { CSSTransition } from 'react-transition-group';
import transitions from './PhotoZoomViewerTransition.module.scss';
import PhotoZoomBackdrop from './PhotoZoomBackdrop/PhotoZoomBackdrop';
const PhotoZoomViewer = ({ show, photoUrl, onExit}) => (
<CSSTransition in={show} timeout={500} classNames={transitions} unmountOnExit>
<PhotoZoomBackdrop show={show} />
<div className={styles.photoZoomViewer}>
<img className={styles.zoomedImage} src={photoUrl} alt={photoUrl} onClick={onExit} />
</div>
</CSSTransition>
);
export default PhotoZoomViewer;
Here's PhotoZoomViewer's transition specific SCSS:
.enter {
opacity: 0;
&Active {
opacity: 1;
transition: all 300ms ease-in-out;
}
}
.exit {
opacity: 1;
&Active{
opacity: 0;
transition: all 300ms ease-in-out;
}
}
Here's PhotoZoomViewer's own SCSS:
.photoZoomViewer {
z-index: 300;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
cursor: zoom-out;
}
Here's PhotoZoomBackdrop component:
import React from 'react';
import styles from './PhotoZoomBackdrop.module.scss';
const PhotoZoomBackdrop = ({show}) => (
show && <div className={styles.backdrop}></div>
);
export default PhotoZoomBackdrop;
Here's PhotoZoomBackdrop.module.scss:
.backdrop {
width: 100%;
height: 100%;
position: fixed;
z-index: 100;
left: 0;
top: 0;
background-color: rgba(0, 0, 0, 0.2);
}
Without CssTransition component backdrop and image get displayed but with the CSSTransition backdrop covers the entire page and image doesnt show up.
I had to enclose JSX within CSSTransition within a div:
const PhotoZoomViewer = ({ show, photoUrl, onExit}) => (
<CSSTransition in={show} timeout={500} classNames={transitions} unmountOnExit>
<div>
<PhotoZoomBackdrop show={show} />
<div className={styles.photoZoomViewer}>
<img className={styles.zoomedImage} src={photoUrl} alt={photoUrl} onClick={onExit} />
</div>
</div>
</CSSTransition>
);

State working only in console and not changed color in the page

I am trying to change background color of Navbar to white on scrolling, but my code changes the state only in the console.
How to solve it?
Here is React and CSS code
import React, { useState, useEffect } from 'react';
import '../index';
export default function Header() {
const [headerColor, setHeaderColor] = useState('.header container');
const listenScrollEvent = () => {
window.scrollY > 250 ? setHeaderColor('#29323c') : setHeaderColor('transparent');
};
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
window.addEventListener('scroll', listenScrollEvent);
});
console.log(headerColor);
return (
<section id="header" style={{ color: headerColor }}>
<div className="header container" style={{ color: headerColor }}>
/* Header section */
#header {
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100vw;
height: auto;
}
#header .header {
min-height: 8vh;
background-color: rgba(31, 30, 30, 0.24);
transition: 0.3s ease background-color;
}
#header .nav-bar {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 100%;
max-width: 1300px;
padding: 0 10px;
}
I would rephrase your question to: "How does state work in React?"
You seem to be using a component named "Header".
import React, { useState, useEffect } from 'react';
import '../index';
export default function Header() {
const headerColor = useState({ headerColor : 'transparant' });
const listenScrollEvent = () => {
window.scrollY > 250 ? setHeaderColor('#29323c') : setHeaderColor('transparent');
};
//well lets create a func for setHeaderColor
setHeaderColor(color){
this.setState({
headerColor: color
});
}
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
window.addEventListener('scroll', listenScrollEvent);
});
console.log(headerColor);
return (
<section id="header" style={{ background-color: headerColor }}>
<div className="header container" style={{ background-color: headerColor }}>
"Hooks are a new feature in React v16.7.0-alpha useState is the “Hook”. useState() set the default value of the any variable and manage in function component(PureComponent functions). ex : const [count, setCount] = useState(0); set the default value of count 0. and u can use setCount to increment or decrement the value. onClick={() => setCount(count + 1)} increment the count value.DOC
"
Base answer on:
https://stackoverflow.com/a/53166036/4095631
Please also look at:
"Updating an object with setState in React"
Updating an object with setState in React
and the official documents about state and hooks:
https://reactjs.org/docs/hooks-overview.html

Use Css modules add conditional transition styling to my side navigation

I would like to add some transition styling to my side navigation on my app. I am able to do this using normal classes however in this tutorial they use css modules and i am unsure how to do this using css modules.
I would like my nav to glide in and out, at the moment it jumps statically when the onClick function fires - toggleSideDrawer.
I have used this logic but I am not sure if it is doing anything:
className={props.toggleSideDrawer ? classes.SideDrawerOpen : classes.SideDrawer
Essentially i want that when the user clicks the toggle, the transform property switches from translateX(-100%) to translateX(0) but this is not happening.
Side nav code:
import React from "react";
import Logo from "../../Logo/Logo";
import NavigationItems from "../NavigationItems/NavigationItems";
import Backdrop from "../../UI/Backdrop/Backdrop";
import Aux from "../../../hoc/Aux";
import classes from "./SideDrawer.css";
const SideDrawer = props => {
return (
<Aux classname={classes.SideDrawer}>
<Backdrop
showBackdrop={props.showSideDrawer}
clicked={props.toggleSideDrawer}
/>
{props.showSideDrawer && (
<div
onClick={props.toggleSideDrawer}
className={
props.toggleSideDrawer ? classes.SideDrawerOpen : classes.SideDrawer
}
>
<div className={classes.Logo}>
<Logo />
</div>
<nav>
<NavigationItems />
</nav>
</div>
)}
</Aux>
);
};
export default SideDrawer;
Where the code is used in my Layout component:
import React, { useState } from "react";
import Aux from "../Aux";
import classes from "./Layout.css";
import Toolbar from "../../components/Navigation/Toolbar/Toolbar";
import SideDrawer from "../../components/Navigation/SideDrawer/SideDrawer";
const layout = props => {
const [showSideDrawer, setShowSideDrawer] = useState(false);
return (
<Aux>
<SideDrawer
showSideDrawer={showSideDrawer}
toggleSideDrawer={() => {
setShowSideDrawer(!showSideDrawer);
}}
/>
<Toolbar
onMenuClick={() => {
setShowSideDrawer(!showSideDrawer);
}}
/>
<main className={classes.mainContent}> {props.children} </main>
</Aux>
);
};
export default layout;
CSS:
.SideDrawer {
position: fixed;
width: 280px;
max-width: 70%;
height: 100%;
left: 0;
top: 0;
z-index: 200;
background-color: white;
padding: 32px 16px;
box-sizing: border-box;
transform: translateX(-100%);
}
#media (min-width: 500px) {
.SideDrawer {
display: none;
}
}
.Logo {
height: 11%;
text-align: center;
}
.SideDrawerOpen {
position: fixed;
width: 280px;
max-width: 70%;
height: 100%;
left: 0;
top: 0;
z-index: 200;
padding: 32px 16px;
box-sizing: border-box;
background-color: red;
transform: translateX(0);
transition: transform 0.3s ease-out;
}
The thing is that you need the element will has the transition rule all the time.
My suggestion is to set a static class which which will hold all the styles and addd another one only for overriding transform to make it move.
Something like that (it uses scss but it's easy to do it with css)
.SideDrawer {
position: fixed;
width: 280px;
max-width: 70%;
height: 100%;
left: 0;
top: 0;
z-index: 200;
background-color: white;
padding: 32px 16px;
box-sizing: border-box;
transition: transform .3s ease;
transform: translateX(-100%);
&.show {
transform: translateX(0);
}
}
export const App = () => {
const [showSideDrawer, setShowSideDrawer] = useState(false);
const sidebarClasses = classname([
styles.SideDrawer,
{
[styles.show]: showSideDrawer
}
]);
const ToggleSidebar = () => {
return (
<button onClick={() => setShowSideDrawer(!showSideDrawer)}>
Toggle Sidebar
</button>
);
};
return (
<Fragment>
<h1>App</h1>
<div className={sidebarClasses}>
<div>Sidebar content</div>
<ToggleSidebar />
</div>
<ToggleSidebar />
</Fragment>
);
};
https://codesandbox.io/s/xenodochial-framework-04sbe?file=/src/App.jsx
#MoshFeu helped me fix this.
The problem is that you render the drawer only when showSideDrawer so before it becomes true, the sidebar is not in the DOM yet so the transition is not affecting it.
The solution is to keep it in the DOM all the time but toggle . Open class to change the style.
There are libraries that knows to make the transition works even for elements that are not in the DOM but it's a bit more complicated.
code fix for SideDrawer.js without the conditional within the return
class SideDrawer extends Component {
render() {
let sideDrawerClass = [classes.SideDrawer];
// SideDrawer will now be an array with the side drawer classes and the open class
if (this.props.showSideDrawer) {
sideDrawerClass.push(classes.Open);
}
return (
<Aux classname={classes.SideDrawer}>
<Backdrop
showBackdrop={this.props.showSideDrawer}
clicked={this.props.toggleSideDrawer}
/>
<div
className={sideDrawerClass.join(" ")}
onClick={this.props.toggleSideDrawer}
>
<div className={classes.Logo}>
<Logo />
</div>
<nav>
<NavigationItems />
</nav>
</div>
</Aux>
);
}
}
export default SideDrawer;

Make cards and images the same size - how can I do it with CSS?

I'm using react. Material-ui is for Cards. For Grid I'm using CSS Grid Layout. So far it looks like this:
But my goal is something like this:
And I have 2 problems:
I want to have all these cards the same height (415px). I tried height: 415px on .BeerListingScroll-info-box but it doesn't work.
Images of bottles and kegs are diffrent in size [keg (80px x 160px) vs. bottle (80px x 317px)]. Is there any way to make them more similar in rendered size?
-
Code:
BeerListingScroll
import React, { Component } from 'react';
import ReduxLazyScroll from 'redux-lazy-scroll';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { fetchBeers } from '../../actions/';
import BeersListItem from '../../components/BeersListItem';
import ProgressIndicator from '../../components/ProgressIndicator';
import './style.css';
class BeerListingScroll extends Component {
constructor(props) {
super(props);
this.loadBeers = this.loadBeers.bind(this);
}
loadBeers() {
const { skip, limit } = this.props.beers;
this.props.fetchBeers(skip, limit);
}
render() {
const { beersArray, isFetching, errorMessage, hasMore } = this.props.beers;
return (
<div className="container beers-lazy-scroll">
<ReduxLazyScroll
isFetching={isFetching}
errorMessage={errorMessage}
loadMore={this.loadBeers}
hasMore={hasMore}
>
<div className="BeerListingScroll-wrapper">
{beersArray.map(beer => (
<div key={beer.id} className="BeerListingScroll-info-box">
<BeersListItem beer={beer} />
</div>
))}
</div>
</ReduxLazyScroll>
<div className="row beers-lazy-scroll__messages">
{isFetching && (
<div className="alert alert-info">
<ProgressIndicator />
</div>
)}
{!hasMore &&
!errorMessage && (
<div className="alert alert-success">
All the beers has been loaded successfully.
</div>
)}
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
beers: state.beers,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ fetchBeers }, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(BeerListingScroll);
BeerListingScroll css
.BeerListingScroll-wrapper {
display: grid;
margin: 0;
grid-gap: 10px;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr) ) ;
background-color: #f7f7f7;
}
.BeerListingScroll-info-box {
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto;
color: #fff;
border-radius: 5px;
padding: 20px;
font-size: 150%;
width: 320px;
}
/* This applies from 600px onwards */
#media (min-width: 1820px) {
.BeerListingScroll-wrapper {
margin: 0 400px;
}
}
#media (min-width: 1620px) {
.BeerListingScroll-wrapper {
margin: 0 300px;
}
}
#media (min-width: 1366px) {
.BeerListingScroll-wrapper {
margin: 0 200px;
}
}
BeerListItem is the child of BeerListingScroll
import React from 'react';
import Card, { CardContent } from 'material-ui/Card';
import Typography from 'material-ui/Typography';
function BeerListItem(props) {
return (
<div>
<Card raised>
<CardContent>
<img src={props.beer.image_url} alt="beer" width="30%" />
<Typography variant="headline" component="h2">
{props.beer.name}
</Typography>
<Typography component="p">{props.beer.tagline}</Typography>
</CardContent>
</Card>
</div>
);
}
export default BeerListItem;
Full project on github -> Github
So for image sizes here I got great answer.
And I added:
.BeerListItem-img {
height: auto;
max-height: 250px;
width: auto;
max-width: 250px;
}
And for card size I just added inside BeerListItem class to Card like so (.BeerListItem-main-card):
function BeerListItem(props) {
return (
<div>
<Card raised className="BeerListItem-main-card">
<CardContent>
<img
src={props.beer.image_url}
alt="beer"
className="BeerListItem-img"
/>
<Typography variant="headline" component="h2">
{props.beer.name}
</Typography>
<Typography component="p">{props.beer.tagline}</Typography>
</CardContent>
</Card>
</div>
);
}
And here is corresponding css to that component.
.BeerListItem-main-card {
width: 320px;
height: 415px;
}
.BeerListItem-img {
height: auto;
max-height: 250px;
width: auto;
max-width: 250px;
}
With that two changes, I've managed to achieve my goal.
You should try exploring display:flex;
Here is a link to a fantastic code pen that may help you achieve what you want:
https://codepen.io/enxaneta/full/adLPwv
More specifically here is an example I've created with what you might be trying to achieve.
https://jsfiddle.net/dalecarslaw/sxdr3eep/
Here is the areas of code you should focus on:
display:flex;
align-items:space-between;
justify-content:space-between;
flex-wrap:wrap;

Categories

Resources