Hi the following code should modify the visibility of a react component depending on whether or not a button is clicked, this when a button is clicked the first element must disappear and I have to appear the second, but this does not happen what is this due to?
React code:
import React from "react";
import styled from "styled-components";
import image from "../../assets/img/calc.png";
import CalculatorContainer from "./CalculatorDraggable/index";
class DraggableCalculator extends React.Component {
constructor(props) {
super(props);
this.state = { isCalculatorVisible: false };
}
enable() {
console.log("Make calculator visibile");
this.setState({
isCalculatorVisible: true,
});
}
render() {
return (
<p>
{this.state.isCalculatorVisible == true ? (
<CalculatorContainer />
) : (
<p></p>
)}
<Container onClick={this.enable}>
<img
key={Math.random()}
src={image}
alt="help"
width={120}
height={220}
style={{ marginLeft: 20 }}
/>
</Container>
</p>
);
}
}
export default DraggableCalculator;
const Container = styled.div`
border-radius: 25px;
border: 2px solid #73ad21;
padding: 20px;
width: 200px;
height: 200px;
/* background-color: var(--cLight2);
border: 5px solid var(--cMain); */
border-radius: 50%;
margin-left: auto;
margin-top: 30px;
margin-right: auto;
cursor: pointer;
// #MEDIA TABLET LANDSCAPE
#media only screen and (min-device-width: 768px) and (max-device-width: 1024px) and (orientation: landscape) {
width: 100px;
height: 100px;
}
img {
max-width: 100%;
max-height: 100%;
}
`;
It looks like the enable function doesn't have the this of the class component bound to it, it throws an error TypeError Cannot read properties of undefined (reading 'setState').
Either bind this to it in the constructor
constructor(props) {
super(props);
this.state = { isCalculatorVisible: false };
this.enable = this.enable.bind(this); // <--
}
or convert to an arrow function so it happens automatically
enable = () => {
console.log("Make calculator visible");
this.setState({
isCalculatorVisible: true,
});
}
Your code is not working because "this" is undefined inside the enable() function. It so because React component (DraggableCalculator, in your case) does not auto-bind its methods to itself.
Hence you have to bind any method yourself inside the constructor, like this for your case:
constructor(props) {
super(props);
this.state = { isCalculatorVisible: false };
this.enable = this.enable.bind(this);
}
Related
import Header from './components/Header';
import Bio from './components/Bio';
import styled from 'styled-components';
import Photo from './components/Photo';
class App extends Component {
constructor(props) {
super(props);
this.state = {
name: "John Smith",
job: "Noob Developer",
email: "John#Smith.noob",
university: "Harvard University",
study: "Law",
year: "2020",
experience: ["Google","Facebook","Airbnb"],
input: false,
}
this.switchShow = this.switchShow.bind(this);
}
switchShow () {
this.setState( prevState => ({
input: !prevState.input
}))
console.log(this.state.input)
}
render() {
let {name,job,email,university,study,year,experience,input} = this.state
return(
<div className="App">
<Header/>
<FlexRowContainer>
<EditButton onClick={this.switchShow}>Edit</EditButton>
<Photo/>
<Bio name={name} job={job} email={email} school={university} study={study} year={year} experience={experience} input={input}>
</Bio>
</FlexRowContainer>
</div>
)
}
}
export default App;
const FlexRowContainer = styled.div`
display: flex;
width: 100%;
flex-direction: row;
justify-content: center;
`
const EditButton = styled.button`
float: right;
width: auto;
height: auto;
position: absolute;
border: transparent;`
So I tried to change this.state.input using switchShow method on and after change the component didn't render even though when I console.log(this.state.input) it succesfully change from false to true or when clicked again it changes from true to false again. Is there anything wrong?
Bio component is down here
import styled from 'styled-components'
class Bio extends Component {
constructor(props) {
super(props)
this.state = {
name: this.props.name,
job: this.props.job,
email: this.props.email,
school: this.props.school,
study: this.props.study,
yearClass: this.props.year,
experience: this.props.experience,
input: this.props.input,
};
}
render() {
let {name,job,email,school,study,yearClass,experience,input} = this.state
return (
<div>
<StyledBioContainer>
<StyledSubtitle>Name</StyledSubtitle>
{ !input ? <StyledParagraph>{name}</StyledParagraph> : <input></input>}
<StyledSubtitle>Job</StyledSubtitle>
{ !input ? <StyledParagraph>{job}</StyledParagraph> : <input></input>}
<StyledSubtitle>Email</StyledSubtitle>
{ !input ? <StyledParagraph>{email}</StyledParagraph> : <input></input>}
<StyledSubtitle>School</StyledSubtitle>
{ !input ? <StyledParagraph>{school}</StyledParagraph> : <input></input>}
<StyledSubtitle>Title of Study</StyledSubtitle>
{ !input? <StyledParagraph>{study}</StyledParagraph> : <input></input>}
<StyledSubtitle>Class</StyledSubtitle>
{ !input? <StyledParagraph>{yearClass}</StyledParagraph> : <input></input>}
<StyledSubtitle>Experiences</StyledSubtitle>
{ !input? experience.map(experience => <StyledParagraph>{experience}</StyledParagraph>) : <input></input>}
</StyledBioContainer>
</div>
)
}
}
export default Bio;
const StyledBioContainer = styled.div`
display: flex;
font-family: Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;
flex-direction: column;
width: 100%;
padding: 3rem;
color: black;
height: auto;
background-color: rgba(0,105,255,.05);
text-align: center;
border-radius: 3px;
margin-top: 1.5rem;
`
const StyledSubtitle = styled.h6`
font-family: Verdana, Geneva, Tahoma, sans-serif;
font-size: 12px;
margin-top: 10px;
margin-bottom: 0px;
color: gray;
`
const StyledParagraph = styled.p`
margin-top: 0.75rem;
margin-bottom: 5px;
font-size: 20px;
The issue is that in the Bio component, you are assigning the props to the state variables in the constructor and then do conditional render on the state of the Bio. When the components is created, it takes the props and assigns them to the state, but when you change the props, the constructor is never called again.
You can either skip setting the state from props and use props in your render, or if you want to use class component, you can call componentDidUpdate, and update your state with new props.
Here is working example with using props instead of state for conditional render in Bio component
https://codesandbox.io/s/focused-rosalind-8rnih?file=/src/Bio.jsx
The problem comes from your <Bio /> component not listening to props changes to update it's internal state.
You can fix this by adding this to your <Bio /> component:
componentDidUpdate(prevProps) {
if (this.props.input !== prevProps.input)
this.setState({input: this.props.input})
}
Here's a full example: https://stackblitz.com/edit/react-vums1a?file=src/App.js
Edit: Didn't see #szczocik answer while I was typing mine, but you can also do has suggested and not use a different state in <Bio /> and use props instead. It will achieve the same result, but you'll lose the local state of <Bio /> so it really depends on wether you need one in the first place or not.
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)
);
}
I want to show different data or hide component if it doesn't fit on screen.
I made a working example but i don't think it's a right/elegant way to do it.
Maybe someone can show me a better way?
This image of example that i made in codepen.
1. I want to hide first red block if it's doesn't fit in grey.
I don't want to do it on media queries of window-size because my red blocks maybe be different in size from user to user.
Codepen example (resize to hide block): https://codepen.io/bofemptiness/pen/YJRKGj
const styled = styled.default
const Component = React.Component;
const DivHat = styled.div`
position: fixed;
top: 0;
width: 100% ;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
background-color: blue;
`
const DivProfile = styled.div`
display: flex;
height: 100%;
cursor: pointer;
`
const ExpCooDiv = styled.div`
display: flex;
flex-direction: column;
font-size: 1rem;
`
class App extends Component {
constructor(props) {
super(props)
this.state = { showCookies: true, lvlRefLastSize: 0 }
this.statsRef = React.createRef()
this.cocRef = React.createRef()
this.lvlRef = React.createRef()
this.mediaStats = this.mediaStats.bind(this);
}
componentDidMount() {
// Add listner when window resizes
window.addEventListener('resize', this.mediaStats)
// Activate function at least one time on load
this.mediaStats()
}
componentWillUnmount() {
window.removeEventListener('resize', this.mediaStats)
}
// Show/hide first red block if summ of red blocks widths <= width of grey one
mediaStats = () => {
console.log(this.statsRef.current.scrollWidth)
if (this.lvlRef.current.scrollWidth != 0)
this.setState({ lvlRefLastSize: this.lvlRef.current.scrollWidth })
if (this.statsRef.current.scrollWidth <= this.state.lvlRefLastSize + this.cocRef.current.scrollWidth) {
this.setState({ showCookies: false })
} else {
this.setState({ showCookies: true })
}
}
render () {
return(
<DivHat>
<div>Menu</div>
<div id='test' ref={this.statsRef}>
<div ref={this.lvlRef} id='test2'>
{this.state.showCookies &&
<React.Fragment>
<span>DATA that i hide</span>
</React.Fragment>
}
</div>
<div ref={this.cocRef} id='test2'>
ANOTHER DATA
</div>
</div>
<DivProfile >
<div> Profile </div>
</DivProfile>
</DivHat>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
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;
}
I have a some popup block or modal window as you like. And I want that it will close after I press on button. Button will be visible after checkboox will be true. Help me pls. May be I have to add something to css, or JS code is incorrect.
Code is below.
class ModalWindow extends React.Component {
constructor() {
super();
this.state = {
open: false,
checked: false
};
this.handleChange = this.handleChange.bind(this);
}
handleChange() {
this.setState({
checked: !this.state.checked
})
}
hide() {
this.setState({
open: false,
});
}
show() {
this.setState({
open: true,
});
}
componentDidMount() {
this.show();
}
render() {
const buttonContent = this.state.checked ? <div className={s.showButton}>
<button onClick={() => this.hide()} className={s.closeBtn}>Confirm yes yes</button>
</div> : null;
return (
<div className={this.state.open ? 'show':'hide'}>
<div className={s.modal}>
<h2 className={s.modalText}>Some text in block</h2>
<label>I want to confirm</label>
<input type="checkbox" checked={this.state.checked} onChange={this.handleChange}/>
{buttonContent}
</div>
</div>
);
}
}
export default withStyles(s)(ModalWindow);
.modal {
background:#fff;
width: 350px;
height: 200px;
margin: 5% auto;
padding: 5px 20px;
position: relative;
border: 2px solid #0000ee;
}
.hide {
display:none
}
.modalText {
font-size: 18px;
color: #000000;
}
label {
margin:0 15px 0 0;
}
.closeBtn {
display: block;
position: absolute;
bottom: 5px;
width: 150px;
height:50px;
margin:0 0 0 100px;
outline: none;
color: #555;
border: none;
background: #000000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
With react you have another way to do hiding and showing of elements. You just render it or you don't.
So instead of setting the state inside the modal dialog to show or hide the modal dialog you should have a property outside of it which decides if this dialog is rendered. Your React App should look something like this:
class ComponentWithModalDialog extends React.Component {
render() {
const {showModal} = this.props;
if(showModal) {
return <ModalWindow />
}
else {
return <div>
other content
</div>
}
}
}