Change a prop on a styled component and update appearance - reactjs

I'm very new to styled components (and I'm not great with React in general) and I can't quite get my head around them. I've created a basic example which is an abstraction of what I want to achieve. When I click on the box, I want the property on to be changed to true and for the colour of <Box> to be updated to green as per the background-color rule.
How do I achieve this? Especially in the instance where there could be an indeterminate number of boxes?
Component
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
const Box = styled.a`
display: block;
height: 100px;
width: 100px;
background-color: ${props => props.on ? 'green' : 'red' };
`;
Box.propTypes = {
on: PropTypes.bool,
onClick: PropTypes.func,
}
Box.defaultProps = {
on: false,
onClick: () => {},
}
export default Box;
Implementation
<Box on={ false } onClick={ }></Box>

// App.js
import React from "react";
import ReactDOM from "react-dom";
import Test from "./Test";
class App extends React.Component {
state = {
on: false
};
handleChange = () => {
this.setState(prevState => ({ on: !prevState.on }));
};
render() {
return (
<div className="App">
<Test on={this.state.on} onClick={this.handleChange}>
Hey
</Test>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
// Test.js
import PropTypes from "prop-types";
import styled from "styled-components";
const Box = styled.a`
display: block;
height: 100px;
width: 100px;
background-color: ${props => (props.on ? "green" : "red")};
`;
Box.propTypes = {
on: PropTypes.bool,
onClick: PropTypes.func
};
Box.defaultProps = {
on: false,
onClick: () => {}
};
export default Box;

You would handle the state in its parent component. For example you could do something like this:
class Page extends Component {
state = { on: false }
handleClick = () => {
this.setState(prevState => ({ on: !prevState.on }));
}
render() {
return <Box on={this.state.on} onClick={this.handleClick} />
}
}
Or even simpler using React hooks:
const Page = () => {
const [on, setOn] = useState(false);
return <Box on={on} onClick={() => setOn(on => !on)} />;
};
Here's an example of what you could do if you wanted 10 boxes
(note: creating the onClick handler in the render method like I did could cause performance issues if you have a very large number of boxes)
class Page extends Component {
state = { boxes: Array(10).fill(false) }
handleClick = (index) => {
this.setState(prevState => ({
boxes: [
...prevState.boxes.slice(0, index),
!prevState.boxes[index],
...prevState.boxes.slice(index)
]
}));
}
render() {
return (
<React.Fragment>
{this.state.boxes.map((on, index) =>
<Box on={on} onClick={() => this.handleClick(index)} />
)}
</React.Fragment>
);
}
}

Related

props is undefined in react js when used in the child class

im trying to send a value from parent class to child but for some reason im getting props undefined any help?
this is parent class and sending the data to child.
I have added the full child code as requested. im not sure how to add props in as it is a function and when i tried adding props in im still getting props undefined error
{this.state.viewProfile ? (
<SideProfileDrawer
people={this.state.people}
viewprof={this.state.viewProfile}
/>
) : null}
here is me using the props in child
import { makeStyles } from "#material-ui/core/styles";
import InstructionsModal from "./instructionsmodal";
import List from "#material-ui/core/List";
import Divider from "#material-ui/core/Divider";
import React, { Component } from "react";
import Tooltip from "#material-ui/core/Tooltip";
import Drawer from "#material-ui/core/Drawer";
import Zoom from "#material-ui/core/Zoom";
import UserProfile from "../quiz/userProfile";
import { Button } from "react-bootstrap";
import { render } from "react-dom";
import Test from "../quiz/test";
export default function TemporaryDrawer(props) {
const useStyles = makeStyles({
list: {
width: 680
},
fullList: {
width: "auto"
}
});
const classes = useStyles();
const [state, setState] = React.useState({
top: false,
left: false,
bottom: false,
right: false
});
const toggleDrawer = (side, open) => event => {
if (
event.type === "keydown" &&
(event.key === "Tab" || event.key === "Shift")
) {
return;
}
setState({ ...state, [side]: open });
};
const sideList = side => (
<div
className={classes.list}
role="presentation"
onClick={toggleDrawer(side, false)}
onKeyDown={toggleDrawer(side, false)}
>
{this.props.people.map((person, index) => {
return (
<UserProfile
className="userProfile"
levelRook={person.levelRook}
levelStudent={person.levelStudent}
levelIntermediate={person.levelIntermediate}
levelExpert={person.levelExpert}
levelMaster={person.levelMaster}
score={person.Score}
question={person.Questions}
email={person.email}
time={person.lastLogin}
/>
);
})}
</div>
);
return (
<div>
{this.props.viewprof?sideList:null}
<Button onClick={toggleDrawer("right", true)}>Open Right</Button>
<Drawer
anchor="right"
open={state.right}
onClose={toggleDrawer("right", false)}
>
{sideList("right")}
</Drawer>
</div>
);
}
Any help in solving this i tried everything
don't use this.props just use props as you have stateless component not stateful read more

Add Emoji from emoji picker to react slate

I use Two package
slate-react and emoji-mart
I want to when choose an Emoji , it puts on my editor.
import React from "react";
import { render } from "react-dom";
import { Editor } from "slate-react";
import { initialValue } from "./initialValue";
// Define our app...
class MyEditor extends React.Component {
// Set the initial value when the app is first constructed.
state = {
value: initialValue
};
// On change, update the app's React state with the new editor value.
onChange = ({ value }) => {
this.setState({ value });
};
onKeyDown = (event, change) => {
// This used to have stuff in it, but I moved it all to plugins.
};
clickMe=()=>{
this.setState({ value : this.state.value });
};
// Render the editor.
render() {
return (
<div>
<h1 onClick={this.clickMe}>Slate Editor Demo</h1>
<div style={{ border: "1px solid black", padding: "1em" }}>
<Editor
value={this.state.value}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
renderNode={this.renderNode}
spellCheck={false}
/>
</div>
</div>
);
}
}
export default MyEditor;
import React,{useState} from 'react';
import 'emoji-mart/css/emoji-mart.css';
import { Picker } from 'emoji-mart';
function Emoji() {
const [emoji,setEmoji] = useState(null);
const addEmoji = (e) => {
setEmoji(e.native)
};
return <Picker onSelect={addEmoji} />
}
export default Emoji;
Try passing the editor ref to picker. Then in Emoji component in addEmoji method, try editorRef.current.InsertText(e.native). After hours of trying to solve this:
const YourTextEditor = props => {
const editor = createEditor();
const addEmoji = async emoji => {
await setTimeout(() => {
editor.focus();
}, 100);
editor.insertText(emoji.native);
};
return (
<>
<Editor
value={initialValue}
/>
<Emoji addEmoji={addEmoji} />
</>
);
};
const Emoji = props => {
return (<Picker onSelect={e => props.addEmoji(e)} />);
};

How to get parent ref in functional component

I've trying to use addEventListener in the Functional component in order to attach the onclick event to its parent.
So when its parent(the red box) is clicked the console.log("Test") is should prints.
At first I should get a ref of it to access the its parent.
So I tried:
https://codesandbox.io/s/red-framework-vv9j7
import React, { useRef, useEffect } from "react";
interface ContextMenuProps {
isVisible: boolean;
}
const ContextMenu: React.FC<ContextMenuProps> = props => {
const thisComponent = useRef<any>(this);
useEffect(() => {
if (thisComponent && thisComponent.current) {
thisComponent.current.addEventListener("click", test);
}
}, []);
const test = () => {
console.log("Test");
};
return <></>;
};
export default ContextMenu;
////////////////////////////////////
function App() {
return (
<div
className="App"
style={{ width: "200px", height: "200px", backgroundColor: "red" }}
>
<Test />
</div>
);
}
But, thisComponent is undefined.

How can I convert popover MATERIAL-UI functional component to class based component?

I'm trying to convert this functional component to class based component. I have tried for several hours but could not find where to place these const variables in component. If someone could write it out in class based component it will highly appreciated.
const useStyles = makeStyles(theme => ({
typography: {
padding: theme.spacing(2),
},
}));
function SimplePopper() {
const classes = useStyles();
const [anchorEl, setAnchorEl] = React.useState(null);
function handleClick(event) {
setAnchorEl(anchorEl ? null : event.currentTarget);
}
const open = Boolean(anchorEl);
const id = open ? 'simple-popper' : null;
return (
<div>
<Button aria-describedby={id} variant="contained" onClick={handleClick}>
Toggle Popper
</Button>
<Popper id={id} open={open} anchorEl={anchorEl} transition>
{({ TransitionProps }) => (
<Fade {...TransitionProps} timeout={350}>
<Paper>
<Typography className={classes.typography}>The content of the Popper.</Typography>
</Paper>
</Fade>
)}
</Popper>
</div>
);
}
export default SimplePopper;
import React, { Component } from "react";
import { createMuiTheme } from "#material-ui/core/styles";
import Typography from "#material-ui/core/Typography";
import Button from "#material-ui/core/Button";
import Fade from "#material-ui/core/Fade";
import Paper from "#material-ui/core/Paper";
import Popper from "#material-ui/core/Popper";
import { withStyles } from "#material-ui/styles";
const theme = createMuiTheme({
spacing: 4
});
const styles = {
typography: {
padding: theme.spacing(2)
}
};
class SimplePopper extends Component {
constructor(props) {
super(props);
this.state = { anchorEl: null, open: false };
}
flipOpen = () => this.setState({ ...this.state, open: !this.state.open });
handleClick = event => {
this.state.ancherEl
? this.setState({ anchorEl: null })
: this.setState({ anchorEl: event.currentTarget });
this.flipOpen();
};
render() {
const open = this.state.anchorEl === null ? false : true;
console.log(this.state.anchorEl);
console.log(this.state.open);
const id = this.state.open ? "simple-popper" : null;
const { classes } = this.props;
return (
<div>
<Button
aria-describedby={id}
variant="contained"
onClick={event => this.handleClick(event)}
>
Toggle Popper
</Button>
<Popper
id={id}
open={this.state.open}
anchorEl={this.state.anchorEl}
transition
>
{({ TransitionProps }) => (
<Fade {...TransitionProps} timeout={350}>
<Paper>
<Typography className={classes.typography}>
The content of the Popper.
</Typography>
</Paper>
</Fade>
)}
</Popper>
</div>
);
}
}
export default withStyles(styles)(SimplePopper);
First thing one need to understand is, how class based and functional components work. Also, when and where you use it.
In short, I can say functional components are Used for presenting static data. And class based are Used for dynamic source of data.
Here are few links for your reference.
Class based component vs Functional components what is the difference ( Reactjs ) and React functional components vs classical components
To answer your specific question.
import React, { Component } from 'react';
import { withStyles, makeStyles } from '#material-ui/styles';
const useStyles = makeStyles(theme => ({
typography: {
padding: theme.spacing(2),
},
}));
class SimplePopper extends Component {
constructor(props){
super(props)
this.state = { anchorEl: null, setAnchorEl: null }; <--- Here see the state creation
this.handleClick= this.handleClick.bind(this);
}
handleClick(event) {
const { anchorEl, setAnchorEl } = this.state; <--- Here accessing the state
setAnchorEl(anchorEl ? null : event.currentTarget);
}
render() {
const { anchorEl, setAnchorEl } = this.state; <--- Here accessing the state
const open = Boolean(anchorEl);
const id = open ? 'simple-popper' : null;
const { classes } = this.props;
return (
<div>
............Rest of the JSX............
</div>
);
}
}
export default withStyles(useStyles)(SimplePopper);
Note that here I've used withStyles to wrap the style to your component. So, that the styles will be available as classNames.
Explore the difference and convert the rest
This is more enough to begin with.

How do I prevent a keyup event from bubbling into a MUI Snackbar?

We have created a notification system that uses the material ui Snackbar with an action button and close button. I've added a listener event for enter so that specific notification's action will fire and close the Snackbar. The issue I have is that after this action has been performed, the default behavior of chrome still has the button that triggers the notification focused. If enter is pressed, it not only triggers the notification but also the action button in the notification. Any suggestions on how to prevent this?
import React from 'react';
import { connect } from 'react-redux';
import { withStyles } from '#material-ui/core/styles';
import IconButton from '#material-ui/core/IconButton';
import DeleteIcon from '#material-ui/icons/Delete';
import Tooltip from '#material-ui/core/Tooltip';
import { NotifierConfirm, enqueueInfo } from '#paragon/notification-tools';
import { deleteDocument } from '../../actions/documents';
import { getSelectedDocument } from '../../selectors/documents';
import { jobIsLocked } from '../../modules/jobLocking'; // eslint-disable-line
const styles = ({
border: {
borderRadius: 0,
},
});
class DeleteDocument extends React.Component {
state = {
deleteDocumentOpen: false,
}
onDeleteFile = () => {
if (jobIsLocked()) {
return;
}
this.setState({ deleteDocumentOpen: true });
}
closeDeleteDocument = () => {
this.setState({ deleteDocumentOpen: false });
};
onConfirmDelete = () => {
this.props.onDeleteFile(this.props.selectedDocument.id);
this.setState({ deleteDocumentOpen: false });
}
render() {
const { classes } = this.props;
return (
<div>
<Tooltip disableFocusListener id="delete-tooltip" title="Delete Document">
<div>
<IconButton
className={`${classes.border} deleteDocumentButton`}
disabled={(this.props.selectedDocument == null)}
onClick={this.onDeleteFile}
>
<DeleteIcon />
</IconButton>
</div>
</Tooltip>
<NotifierConfirm
open={this.state.deleteDocumentOpen}
onClose={this.closeDeleteDocument}
onClick={this.onConfirmDelete}
message="Are you sure you want to DELETE this document?"
buttonText="Delete"
/>
</div>
);
}
}
const mapStateToProps = (state) => {
const selectedDocument = getSelectedDocument(state);
return {
selectedDocument,
};
};
function mapDispatchToProps(dispatch) {
return {
onDeleteFile: (documentId) => {
dispatch(deleteDocument(documentId));
},
enqueueInfo,
};
}
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(DeleteDocument));
import React from 'react';
import { withStyles, WithStyles, StyleRulesCallback } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import Snackbar from '#material-ui/core/Snackbar';
import IconButton from '#material-ui/core/IconButton';
import CloseIcon from '#material-ui/icons/Close';
import RootRef from '#material-ui/core/RootRef';
interface NotifierConfirmProps {
open: boolean;
onClose: any;
onClick: () => void;
message: string;
messageSecondary?: any;
buttonText: string;
}
type OwnProps = NotifierConfirmProps & WithStyles<typeof styles>;
const styles: StyleRulesCallback = () => ({
snackbar: {
marginTop: 85,
zIndex: 10000000,
'& div:first-child': {
'& div:first-child': {
width: '100%',
},
},
},
close: {
padding: 8,
marginLeft: 8,
},
buttonColor: {
backgroundColor: '#F3D06E',
},
messageDiv: {
width: '100%',
}
});
class NotifierConfirmComponent extends React.Component<OwnProps> {
notifierRef: React.RefObject<{}>;
constructor(props: OwnProps) {
super(props);
// create a ref to store the textInput DOM element
this.notifierRef = React.createRef();
this.focusNotifier = this.focusNotifier.bind(this);
}
keyPressHandler = (event: any) => {
if (!this.props.open) return;
if (event.keyCode === 27) {
this.props.onClose();
}
if (event.keyCode === 13) {
this.props.onClick();
}
}
focusNotifier() {
// Explicitly focus the text input using the raw DOM API
// Note: we're accessing "current" to get the DOM node
// this.notifierRef.current.focus(); this will not work
}
componentDidMount() {
document.addEventListener('keyup', this.keyPressHandler, false);
}
componentWillUnmount() {
document.removeEventListener('keyup', this.keyPressHandler, false);
}
render() {
const { classes } = this.props;
return (
<React.Fragment>
<RootRef rootRef={this.notifierRef}>
<Snackbar
className={classes.snackbar}
anchorOrigin={{
vertical: 'top',
horizontal: 'center',
}}
open={this.props.open}
onClose={this.props.onClose}
ContentProps={{
'aria-describedby': 'message-id',
}}
message={
<div className={classes.messageDiv} id="message-id">
{this.props.message}<br />
{this.props.messageSecondary}
</div>}
action={[
<Button
className={`${classes.buttonColor} confirmActionButton`}
variant="contained"
key={this.props.buttonText}
size="small"
onClick={this.props.onClick}
>
{this.props.buttonText}
</Button>,
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={this.props.onClose}
>
<CloseIcon />
</IconButton>,
]}
/>
</RootRef>
</React.Fragment>
);
}
}
export const NotifierConfirm = withStyles(styles)(NotifierConfirmComponent);
In your callback you should call Event.preventDefault() or Event.stopPropagation(), it's worth notice that this two aren't the same. Here the docs: Prevent Default, Stop Propagation

Resources