React Context - "this" is undefined - javascript

I am using React Context in order to manage a global state.
So I have defined my Context with its Provider and its Consumer.
I have my videoplaying-context.js
import React from "react";
import { createContext } from 'react';
// set the defaults
const VideoContext = React.createContext({
videoPlaying: false,
setPlayingVideo: () => {}
});
export default VideoContext;
In my _app.js I have:
import App from 'next/app'
import { PageTransition } from 'next-page-transitions'
import VideoContext from '../components/videoplaying-context'
class MyApp extends App {
setPlayingVideo = videoPlaying => {
this.setState({ videoPlaying });
};
state = {
videoPlaying: false,
setPlayingVideo: this.setPlayingVideo
}
render() {
console.log('new _app.js defalt page');
const { Component, pageProps, router, state } = this.props
return (
<React.Fragment>
<VideoContext.Provider value={this.state}>
<PageTransition timeout={300} classNames="page-transition">
<Component {...pageProps} key={router.route} />
</PageTransition>
</VideoContext.Provider>
</React.Fragment>
)
}
}
export default MyApp
and then in one of my file I have put the Consumer:
import Layout from "../components/Layout";
import ReactPlayer from 'react-player
import VideoContext from '../components/videoplaying-context'
class Video extends React.Component {
constructor(props) {
super(props);
this.triggerVideo = this.triggerVideo.bind(this);
}
triggerVideo(event) {
console.log("click");
/* doing other stuff here... */
}
render() {
return (
<VideoContext.Consumer>
{context => (
<Layout>
<h1>Videos</h1>
<div>
<div className="group" id="process-video">
<div
className="poster-image"
onClick={() => {
this.triggerVideo.bind(this);
context.setPlayingVideo(true);
}}
/>
<ReactPlayer
url="https://vimeo.com/169599296"
width="640px"
height="640px"
config={{
vimeo: {
playerOptions: {
thumbnail_url: "http://placehold.it/640x640.jpg",
thumbnail_width: 640,
thumbnail_height: 640
}
}
}}
/>
</div>
</div>
<style jsx global>{`
.group {
position: relative;
height: 0;
overflow: hidden;
height: 640px;
width: 640px;
}
.poster-image {
background: url("http://placehold.it/640x640.jpg") center center;
background-size: cover;
bottom: 0;
left: 0;
opacity: 1;
position: absolute;
right: 0;
top: 0;
z-index: 10;
height: 640px;
width: 640px;
transition: all 0.4s ease-in;
}
.poster-image + div {
position: absolute;
top: 0;
left: 0;
width: 640px;
height: 640px;
}
.poster-image.video--fadeout {
opacity: 0;
}
`}</style>
</Layout>
)}
</VideoContext.Consumer>
);
}
}
export default Video;
So, the function "context.setPlayingVideo(true)" is working fine and it's correctly setting the global state "videoPlaying" to true, but, after the introduction of the Context, "this.triggerVideo.bind(this);" is not working anymore because "this" is undefined.
I tried removing it and other stuff but I'm really stuck and I don't know hot to fix it.
Thanks everyone!

On this line you are not calling the method triggerVideo
onClick={() => { this.triggerVideo.bind(this); context.setPlayingVideo(true); }}
Change to:
onClick={() => { this.triggerVideo(); context.setPlayingVideo(true); }}
or to:
onClick={() => { this.triggerVideo.bind(this)(); context.setPlayingVideo(true); }}

Related

Overflow is messing up with scrollY value

I have been trying to read the value of scrollY in my reactapp. But I found that it is being messed by overflow.
Here is the code that I used to read the scrollY:
import React from "react";
import{useEffect, useState} from "react"
export default function Test() {
const [offset, setOffset] = useState(0);
const setScroll = () => {
setOffset(window.scrollY);
};
useEffect(() => {
window.addEventListener("scroll", setScroll);
return () => {
window.removeEventListener("scroll", setScroll);
};
}, []);
return (
<div>
<div style={{color:"red"}}>{offset}</div>
</div>
);
}
Here is my app.jsx >>>
import React, { Component } from 'react';
import { Route } from 'react-router';
import { Layout } from './components/Layout';
import { Home } from './components/Home';
import { FetchData } from './components/FetchData';
import { Counter } from './components/Counter';
import abc2 from './img/abc2.jpg'
import './custom.css'
import Resume from './components/resumePage/Resume';
import Test from './components/Test.jsx';
var backGStyle={
backgroundImage:`url(${abc2})`,
minHeight: "100vh",
backgroundSize:'cover',
color:'red'
}
export default class App extends Component {
static displayName = App.name;
render () {
return (
<div className="app" style={backGStyle}>
<div className="mask">
<Layout>
<Route exact path='/' component={Home} />
<Route path='/counter' component={Counter} />
<Route path='/resume' component={Resume} />
<Route path='/test' component={Test} />
</Layout>
</div>
</div>
);
}
}
and my custom.css >>
a {
color: #0366d6;
}
code {
color: #E01A76;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.backgroundBody{
position: relative;
}
.app .mask{
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
color:#fff;
background-color:rgba(0, 0, 0, 0.6);
overflow:auto;
}
.abc{
color: aqua;
}
And if I remove the overflow: auto from custome.css .app .mask, I am being able to get the value of scrollY but my background image will be messed up, there will be white space in the background of half of the content like this >>>>
try element.scrollTop since your using divs as masks. ScrollY is only for the scroll position of the window (HTML element). Your question is a little vague, if your trying to align you background images use background-position and background-size. also set your overflow to clip.
https://developer.mozilla.org/en-US/docs/Web/CSS/background-position

Expand div in a Class component

I'm following this react-flip-toolkit tutorial in order to animate a<div> expansion in a component:
This is the tutorial code:
import React, { useState } from 'react'
import { Flipper, Flipped } from 'react-flip-toolkit'
const AnimatedSquare = () => {
const [fullScreen, setFullScreen] = useState(false)
const toggleFullScreen = () => setFullScreen(prevState => !prevState)
return (
<Flipper flipKey={fullScreen}>
<Flipped flipId="square">
<div
className={fullScreen ? 'full-screen-square' : 'square'}
onClick={toggleFullScreen}
/>
</Flipped>
</Flipper>
)
}
My project however, unlike the functional Component example above, uses Class components, like so:
class Field extends Component {
constructor(props) {
super(props);
this.state = {
players:[],
};
}
getPlayersByPosition = (players, position) => {
return players.filter((player) => player.position === position);
};
render() {
const { players } = this.props;
if(players){
return (
<div className="back">
<div className="field-wrapper" >
<Output output={this.props.strategy} />
// this is the target div I want to expand
<div className="row">
{this.getPlayersByPosition(players, 5).map((player,i) => (
<Position key={i} >{player.name}</Position>
))}
</div>
</div>
</div>
);
}else{
return null}
}
}
export default Field;
How can I declare AnimatedSquare() in my Class component and encapsulate my target <div> above within <Flipper/> and <Flipped/>?
I've converted the example to a class based component for you. You should be able to work the rest out from this example:
import React, { Component } from "react";
import ReactDOM from "react-dom";
import { Flipped, Flipper } from "react-flip-toolkit";
import "./styles.css";
class AnimatedSquare extends Component {
state = {
fullScreen: false
};
toggleFullScreen() {
this.setState({ fullScreen: !this.state.fullScreen });
}
render() {
const { fullScreen } = this.state;
return (
<Flipper flipKey={fullScreen}>
<Flipped flipId="square">
<div
className={fullScreen ? "full-screen-square" : "square"}
onClick={this.toggleFullScreen.bind(this)}
/>
</Flipped>
</Flipper>
);
}
}
ReactDOM.render(<AnimatedSquare />, document.querySelector("#root"));
* {
box-sizing: border-box;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.square {
width: 5rem;
height: 5rem;
cursor: pointer;
background-image: linear-gradient(
45deg,
rgb(121, 113, 234),
rgb(97, 71, 182)
);
}
.full-screen-square {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
cursor: pointer;
background-image: linear-gradient(
45deg,
rgb(121, 113, 234),
rgb(97, 71, 182)
);
}

React hooks onClick fire only for parent (modal)

I have a modal window and i want it to hide if user clicked on modal itself(black background), but click is trigerring by clildrens to. Here is the example:
import React, { useState, useEffect } from 'react'
import styled from 'styled-components'
export function Modal({ show, children }) {
return (
<DivModal show={show}>
{children}
</DivModal>
)
}
const DivModal = styled.div`
display: ${props => (props.show ? 'block' : 'none')};
`
How to fire event's only for modal itself?
Ehh, this work, but if you click on wraper div - modal will not hide :(
After some googling ant try, found a way. Key point is that you need to stop onClick event propagation from parent to childrens. In my case i just wrapped my component by div with onClick={e => e.stopPropagation()}.
import React from 'react'
import styled from 'styled-components'
export function Modal({ show, showModalSet, children }) {
return (
<DivModal onClick={() => showModalSet(false)} show={show}>
<div onClick={e => e.stopPropagation()}>{children}</div>
</DivModal>
)
}
const DivModal = styled.div`
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
display: ${({ show }) => (show ? 'block' : 'none')};
z-index: 10;
overflow-y: scroll;
`
Component use:
<Modal show={showModal} showModalSet={showModalSetFunctionInUsePlace}>
Another approach, that i'am happy now with is to use refs and compare what component was clicked:
import React, { useRef } from 'react'
import styled from 'styled-components'
export function Modal({ show, showModalSet, children }) {
const modalRef = useRef(null)
function handleClick(e) {
if (e.target == modalRef.current) showModalSet(false)
}
return (
<DivModal onClick={handleClick} show={show} ref={modalRef}>
{children}
</DivModal>
)
}
const DivModal = styled.div`
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
display: ${({ show }) => (show ? 'block' : 'none')};
z-index: 10;
overflow-y: scroll;
`

Modifying child last component and css attribute in ReactJs

I want to show these three images from my Parent component and i am trying to remove the line after Summary breadcrumb .
trying to remove last line
This is my root Class of parent and trying to show only three images but not the line.
This is BCrumb.css file
.root {
color: #fff;
font-size: 12px;
display: flex;
padding: 1px;
justify-content: initial;
margin-left: 1%;
}
This is BCrumb.tsx class
import * as React from "react";
import classes from "./BCrumb.css";
interface IBCrumbProps {
children?: any;
}
class BCrumb extends React.Component<IBCrumbProps, {}> {
render() {
console.log("Children>>>>"+React.Children.count(this.props.children));
return <div className={classes.root}>
{React.Children.map(this.props.children, (child , i) => {
// here i am trying to hide the line after summary but i //dont know how to implement it here
if (i == 3) return
return child
})}
</div>;
}
}
export default BCrumb;
BCItems.css file
.root {
color: #297848;
font-size: 12px;
text-align: center;
margin-left: 13%;
display: flex;
justify-content: space-evenly;
}
.step-title {
color: #297848;
font-size: 12px;
text-align: center;
}
.step-icon.active {
height: 28px;
margin-bottom: 3px;
}
div.disabled {
height: 28px;
opacity: 0.5;
pointer-events: none;
}
.stepconnector {
position: fixed;
height: 1.7px;
width: 3.6%;
margin-top: 2%;
background-color: #ccc;
margin-left: 3.6%;
display: block;
}
BCItems.tsx class
import * as React from "react";
import classes from "./BCItem.css";
import classnames from "classnames";
interface IBCItemProps{
children?: any;
active?: boolean;
inactiveSrc?: boolean;
activeSrc?: boolean;
}
class BCItems extends React.Component<IBCItemProps, {}> {
render() {
const { children, active, activeSrc, inactiveSrc, label } = this.props;
const className = classnames({
[classes.root]: true,
[classes.disabled]: !active
});
//var i = ;
return (
<div className={className}>
<div>
{active ? (
<img className={classes.img1} src={activeSrc} />
) : (
<img className={classes.img1} src={inactiveSrc} />
)}
<p className={classes.labelText}>{label}</p>
</div>
<div className={classes.stepconnector}></div>
</div>
);
}
}
export default BCItems;
This is the class that showing BCrumb items
import * as React from "react";
import BCItems from "../../components/BCrumb/BCItems";
import BCrumb from "../../components/BCrumb/BCrumb";
import Step1_1 from "../../../assets/step-1-active.png";
import Step1_0 from "../../../assets/step-1.png";
import step2_1 from "../../../assets/step-2-active.png";
import step2_0 from "../../../assets/step-2.png";
import step3_1 from "../../../assets/step-3-active.png";
import step3_0 from "../../../assets/step-3.png";
import classes from "./HomePage.css";
class HomePage extends React.Component {
constructor(props) {
super(props);
this.state = { setAct1: true, setAct2: false };
}
render() {
const styles = {
containerStyle: {
paddingLeft: 37
}
};
const { containerStyle } = styles;
return (
<div>
<BCrumb>
<BCItems
active={true}
activeSrc={Step1_1}
inactiveSrc={Step1_0}
label="Profile"
/>
<BCItems
active={true}
activeSrc={Step2_1}
inactiveSrc={Step2_0}
label="DashBoard"
/>
<BCItems
active={true}
activeSrc={Step3_1}
inactiveSrc={Step3_0}
label="Summary"
/>
</BCrumb>
</div>
);
}
}
export default HomePage;
I dont know how to hide the last item of css element (line) from the parent class using React.Children.map
Use last-child, a CSS selector:
.root:last-child .stepconnector {
display: none !important;
}

Material-ui drawer width issue

I'm facing an issue with material-ui drawer. I've changed the width of the drawer container which causes a a problem . The drawer remains a little inside the page and visible but I don't want to make it visible on the page while I haven't clicked the button. It might be having an issue with the transform attribute now.
So I changed it to transform: translate(350px, 0px) but then I'm getting another issue, that is if I am clicking the button the drawer is not showing up. Any help on this thing ??
I have got the solution and edited the code.
I've created a Demo here => Have a look
Also shared the code below:
index.js
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
import Drawer from 'material-ui/Drawer';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
class App extends Component {
constructor() {
super();
this.state = {
openDrawer: false
};
}
toggleDrawer() {
this.setState({
openDrawer: !this.state.openDrawer
});
}
render() {
return (
<MuiThemeProvider>
<div>
<button onClick={this.toggleDrawer.bind(this)}> Toggle Drawer</button>
<Drawer
open={this.state.openDrawer}
containerClassName={!this.state.openDrawer? "hide-drawer": "show-drawer" }
openSecondary={true}
docked={true}
>
<div className="drawer-title-div">
<h4 className="drawer-title-text">It's my drawer</h4>
</div>
</Drawer>
</div>
</MuiThemeProvider>
);
}
}
render(<App />, document.getElementById('root'));
style.css
h1, p {
font-family: Lato;
}
.show-drawer {
top: 47px !important;
text-align: left !important;
width: 80% !important;
transform: translate(0%, 0px) !important;
}
.hide-drawer {
top: 47px !important;
text-align: left !important;
width: 80% !important;
transform: translate(100%, 0px) !important;
}
/* .drawer-side-drawer:focus {
top: 47px !important;
text-align: left !important;
width: 350px !important;
transform: translate(0px, 0px) !important;
} */
.drawer-title-div {
display: inline-block;
width: 100%;
background: #F2F8FB;
box-shadow: 0 1px 3px 0 rgba(0,0,0,0.24);
}
.drawer-title-text {
display: inline-block;
margin-left: 16px;
margin-top: 16px;
margin-bottom: 16px;
color: #484848;
font-family: Muli;
font-size: 16px;
font-weight: 600;
}
For mui version 5, you have to use the PaperProps prop like so:
<Drawer
PaperProps={{
sx: { width: "90%" },
}}
>{...Child elements here}</Drawer>
you can simply add this to index.css
.MuiDrawer-paper {
width: 60% !important;
}
#media (max-width: 1200px) {
.MuiDrawer-paper {
width: 100% !important;
}
}
Just add PaperProps={{ style: { width: '25%' } }} to your MUI Drawer.
Most Probably it will work for everyone.
You can try adding a toggle class and you can get rid of the transform.
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
import Drawer from 'material-ui/Drawer';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
class App extends Component {
constructor() {
super();
this.state = {
openDrawer: false
};
}
toggleDrawer() {
this.setState({
openDrawer: !this.state.openDrawer
});
}
render() {
return (
<MuiThemeProvider>
<div>
<button onClick={this.toggleDrawer.bind(this)}> Toggle Drawer</button>
<Drawer containerClassName={!this.state.openDrawer ? "hide-drawer": "show-drawer"}
open={this.state.openDrawer}
openSecondary={true}
docked={true}
>
<div className="drawer-title-div">
<h4 className="drawer-title-text">It's my drawer</h4>
</div>
</Drawer>
</div>
</MuiThemeProvider>
);
}
}
render(<App />, document.getElementById('root'));
You can use window.innerWidth as width: 100%:
<Drawer ...>
<div style={{width: window.innerWidth * 0.25}}>
...
</div>
</Drawer>
One way to solve this issue is by getting the parent width:
const parentRef = useRef<HTMLDivElement>(null);
<Box
ref={parentRef}
>
<Drawer
PaperProps={{
sx: {
width: parentRef?.current?.clientWidth || 0,
},
}}
// .... etc
</Drawer>
</Box>
One way to solve this issue is by getting the parent width:
const parentRef = useRef<HTMLDivElement>(null);
<Box
ref={parentRef}
>
<Drawer
PaperProps={{
sx: {
width: parentRef?.current?.clientWidth || 0,
},
}}
>
// content goes here
</Drawer>
</Box>
Drawer-Material-UI If you look at the link.. you will find Drawer properties..
width (union: string number) [default : null] The width of the Drawer in pixels or
percentage in string format ex. 50% to fill half of the window or 100%
and so on. Defaults to using the values from theme.
so just update the tag with width and you are good to go,
<Drawer width="50%"></Drawer>
Check it here..
The drawer width is not matching the theme drawer width which was causing the problem.. not the transform CSS attribute.
Just a different approach ^^
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
import Drawer from 'material-ui/Drawer';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import Responsive from 'react-responsive-decorator'; // This decorator allows using the library as a decorator.
#Responsive
class App extends Component {
constructor() {
super();
this.state = {
openDrawer: false,
width:350
};
}
// Update for kinda media query thing
componentDidMount() {
this.props.media({ minWidth: 768 }, () => {
this.setState({
width: 350
});
});
this.props.media({ maxWidth: 768 }, () => {
this.setState({
width: 150
});
});
}
toggleDrawer() {
this.setState({
openDrawer: !this.state.openDrawer
});
}
render() {
return (
<MuiThemeProvider>
<div>
<button onClick={this.toggleDrawer.bind(this)}> Toggle Drawer</button>
<Drawer width={this.state.width} //<- Update
open={this.state.openDrawer}
containerClassName="drawer-side-drawer"
openSecondary={true}
docked={true}
>
<div className="drawer-title-div">
<h4 className="drawer-title-text">It's my drawer</h4>
</div>
</Drawer>
</div>
</MuiThemeProvider>
);
}
}
render(<App />, document.getElementById('root'));
I had the same problem.
you just have to add the PaperProps to your drawer

Categories