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.
Related
I'm trying to implement like function by using redux and map function to use states in other components. I used redux not using useState([]) because I thought it is better way to manage states. I made the button changed when button is clicked. But they are sharing same state so it is changed at the same time. To solve this problem, I think I should add this state in array. And here comes my question. How can I add state declared in redux to manage state separately? And is this way correct? I'll upload concrete code including redux by using codesandbox. I'd appreciate it if you let me know, thanks.
Clothes
import React, { useState } from "react";
import styled from "styled-components";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faHeart } from "#fortawesome/free-solid-svg-icons";
import { faHome } from "#fortawesome/free-solid-svg-icons";
import { useDispatch, useSelector } from "react-redux";
const array = [
{
id: "1",
name: "vest",
img:
"https://shopimg.kakaofriendsgolf.com/live/images/2022/9/7/10/918997_1662515279620.png",
price: "10000"
},
{
id: "2",
name: "shirts",
img:
"https://shopimg.kakaofriendsgolf.com/live/images/2022/8/23/18/551886_1661246483199.png",
price: "12000"
},
{
id: "3",
name: "pants",
img:
" https://shopimg.kakaofriendsgolf.com/live/images/2022/8/22/18/18783_1661159105201.png",
price: "15000"
}
];
export default function Clothes() {
const isClick = useSelector((state) => state.CheckLike.isClick);
const dispatch = useDispatch();
// change Icon
const setHide = (e) => {
dispatch({ type: "False" });
};
const setShow = (e) => {
dispatch({ type: "True" });
};
return (
<Wrap>
<div className="productCard">
{array.map((content, idx) => {
return (
<div key={idx} className="productCard__wrap">
<img src={content.img} className="productCard__img" />
<div className="productCard__name">
<div>
<h3 className="productCard__title">{content.name}</h3>
<div className="productCard__priceWrap">
{content.price
.toString()
.replace(/\B(?=(\d{3})+(?!\d))/g, ",")}
</div>
</div>
<div className="productCard__likeWrap">
{/* show heart and house icon according to state */}
{isClick ? (
<div
onClick={() => {
setHide();
}}
>
<FontAwesomeIcon icon={faHeart} />
</div>
) : (
<div
onClick={() => {
setShow();
}}
>
<FontAwesomeIcon icon={faHome} />
</div>
)}
</div>
</div>
</div>
);
})}
</div>
</Wrap>
);
}
const Wrap = styled.div`
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
overflow: scroll;
.productCard {
display: flex;
position: absolute;
top: calc(50vh - 120px);
left: calc(50vw - 180px);
width: "228px";
height: "351px";
padding-right: 8px;
padding-left: 8px;
border: solid 1px black;
}
.productCard__wrap {
border: 1px solid grey;
line-height: 1.428571429;
background-color: #fff;
}
.productCard__img {
width: 228px;
height: 228px;
}
.productCard__name {
padding-bottom: 30px;
position: relative;
padding: 8px 8px 0 8px;
min-height: 118px;
text-align: left;
}
.productCard__title {
font-size: 18px;
margin-top: 0;
margin-bottom: 2px;
padding-right: 37px;
line-height: 1.3;
margin-block-start: 1em;
margin-block-end: 1em;
}
.productCard__priceWrap {
font-size: 18px;
margin-top: 5px;
vertical-align: middle;
margin: 0 0 9px;
margin-block-start: 1em;
margin-block-end: 1em;
text-align: left;
}
.productCard__likeWrap {
position: absolute;
top: -5px;
right: 0;
padding: 0;
height: 40px;
line-height: 37px;
white-space: normal;
display: inline-block;
margin-bottom: 0;
font-weight: normal;
text-align: center;
}
`;
codesandbox
https://codesandbox.io/s/likepractice-tpcv7l?file=/src/components/Clothes.jsx
There could be many approaches, but assuming that the goal is to make the like button work for individual items and save the liked items in the store, I think the solution of using an array as a state is suitable in the use case.
Forked demo with modification: codesandbox
Configure the state as an array and create matching reducers, here the product id is saved to the state array when liked, but this is an optional approach.
const initailState = {
isClick: [],
};
const CheckLike = (state = initailState, action) => {
switch (action.type) {
case "True":
return {
...state,
isClick: [...state.isClick, action.id],
};
case "False":
return {
...state,
isClick: state.isClick.filter((id) => id !== action.id),
};
default:
return {
...state,
};
}
};
Edit the event handler to dispatch the item id to be added or removed from the state array:
const setHide = (id) => {
dispatch({ type: "False", id });
};
const setShow = (id) => {
dispatch({ type: "True", id });
};
Check if product id content.id is included in the state array as a condition for rendering the icons, and wire up the icons to send content.id to the events:
<div className="productCard__likeWrap">
{isClick.includes(content.id) ? (
<div
onClick={() => {
setHide(content.id);
}}
>
<FontAwesomeIcon icon={faHeart} />
</div>
) : (
<div
onClick={() => {
setShow(content.id);
}}
>
<FontAwesomeIcon icon={faHome} />
</div>
)}
</div>
if I'm guessing correctly your problem is that the button click changes all the buttons.
for this issue, you don't need redux. use redux when you want to manage a global state used in multiple components(ex, session).
solution for your problem is, create a new component for each product card and there manage the state of the button.
and to store which products are on the wishlist you can store that in redux.
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);
}
I am a beginner of React.
I'm practicing using React to pass props from parent to child.
but.I can't pass props.
I just want to pass simple props from parent to child.
My code is here.
https://codesandbox.io/s/props-test-3bgjy?file=/src/Child.js
My code is not showing the error.
But my code doesn't display correctly.
import React, { Component } from "react";
import styled from "styled-components";
import Child from "./Child";
const Button = styled.button`
font-size: 16px;
padding: 16px;
`;
class App extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0,
msg: "state(msg)initial",
flg: true
};
this.doAction = this.doAction.bind(this);
}
doAction() {
this.setState((state) => ({
counter: state.counter + 1,
msg: state.counter,
flg: !state.flg
}));
}
render() {
return (
<div>
<Child msg={this.state.message} flag={this.state.flag} />
<Button onClick={this.doAction}>Click</Button>
</div>
);
}
}
export default App;
import React, { Component } from "react";
import styled from "styled-components";
const Message = styled.p`
font-size: 24pt;
color: #900;
margin: 20px 0px;
padding: 5px;
border-bottom: 2px solid #900;
&[data-primary="true"] {
font-size: 24pt;
color: white;
background-color: #900;
margin: 20px 0px;
padding: 5px;
border-bottom: 2px solid #900;
}
`;
class Child extends Component {
render() {
return (
<div>
<Message data-primary={this.props.flag}>
count: {this.props.msg}
</Message>
</div>
);
}
}
export default Child;
You are have mistake in line <Child msg={this.state.message} flag={this.state.flag} />
Need use state param msg
<Child msg={this.state.msg} flag={this.state.flag} />
https://codesandbox.io/s/props-test-forked-gw69r?file=/src/Parent.js
It looks like you have a typo; change this line:
<Child msg={this.state.message} flag={this.state.flag} />
to
<Child msg={this.state.msg} flag={this.state.flg} />
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'm creating a DropDown List box and each item in the list has a remove (X) button to remove the item from the list. How is it possible to show the remove button "only" when the item is hovered over?
The current code shows the clear button each each item but I only want it to show when the item is hovered over
Sorry, here is the code
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
const ListWrapper = styled.div`
position: absolute;
width: 16rem;
z-index: 1;
background: white;
&:hover {
cursor: pointer;
}
`;
const ListMenu = styled.div`
position: absolute;
width: 100%;
z-index: 1;
background: white;
overflow-x: hidden;
`;
const ListMenuHeader = styled.div`
display: flex;
flex-direction: row;
justify-content: flex-end;
`;
const DropdownText = Text.Link.extend`
padding-top: 3rem;
`;
const DropdownButton = styled.div`
padding: 1 rem 0.75rem;
`;
const ListMenuItem = styled.div`
display: flex;
background-color: grey)};
color: grey};
>[name~=icon] {
right: 0rem;
border-radius: 0;
background: none;
align-items: right;
justify-content: right;
&:hover {
background-color: grey)};
}
&:focus {
outline: none;
}
`;
class ListListMenu extends React.Component {
static propTypes = {
id: PropTypes.string.isRequired,
text: PropTypes.node.isRequired,
items: PropTypes.arrayOf(PropTypes.any).isRequired,
component: PropTypes.func.isRequired,
selectedItem: PropTypes.any,
getItemProps: PropTypes.func.isRequired,
highlightedIndex: PropTypes.number,
closeListMenu: PropTypes.func.isRequired,
};
static defaultProps = {
selectedItem: null,
highlightedIndex: null,
}
onClearClick = (items,item1) => (item) => {
const index = items.indexOf(item1);
if (index > -1) {
items.splice(index, 1);
}
}
render() {
const {
id, text, items, component, selectedItem, getItemProps,
highlightedIndex, closeListMenu,
} = this.props;
return (
<ListWrapper id={id} >
<ListMenuHeader onClick={closeListMenu}>
<DropdownText>{text}</DropdownText>
<DropdownButton
id={`${id}-button`}
>
<Icon type="caret-up" appearance="neutral" />
</DropdownButton>
</ListMenuHeader>
<ListMenu>
{selectedItems.map((item, index) => (
<ListMenuItem
{...getItemProps({
item,
isActive: highlightedIndex === index,
isSelected: _.isEqual(selectedItem, item),
})}
key={index}
>
{React.createElement(component, { item })}
<Button // CLEAR BUTTON
name={item}
id={item}
icon="remove"
onClick={this.onClearClick(items, item)}
circle
display="flat"
appearance="disabled"
id="clear-search-button"
/>
</ListMenuItem>
))}
</ListMenu>
</ListWrapper>
);
}
}
export default ListListMenu;
Here is one way you could probably just have that "x" appear on hover.
Instead of looking for a "hover" event, what about looking for an "onmouseenter" event combined with "onmouseleave"?
Like so...
class Example extends React.Component {
onHover() {
this.refs.deleteX.style.display = "block";
}
onExit() {
this.refs.deleteX.style.display = "none";
}
render() {
return (
<div>
<input onmouseenter={ this.onHover } onmouseleave={ this.onExit } />
<p ref="deleteX">x</p>
</div>
)
}
}
Kind of like this post