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.
Related
I have a google map approach that map itself isn't showing, I have 2 pieces of code the first is Map component itself, that's the code for it:
import React, {
useState,
useEffect,
useRef
} from 'react';
interface MapProps extends google.maps.MapOptions {
style: { [key: string]: string };
onClick?: (e: google.maps.MapMouseEvent) => void;
onIdle?: (map: google.maps.Map) => void;
children?: React.ReactNode;
}
const Map: React.FC<MapProps> = ({
onClick,
onIdle,
children,
style,
...options
}) => {
const MyStyle = {
width: '100%',
height: '600px'
}
const ref = useRef<HTMLDivElement>(null);
const [map, setMap] = useState<google.maps.Map>();
return (
<>
<div ref={ref} />
</>
)
}
export default Map;
The second component is App component, the one which Map is being rendered, its code is:
import Map from './components/mapComponent2';
import {
Wrapper,
Status
} from '#googlemaps/react-wrapper';
function App = () => {
const center = { lat: 30.033333, lng: 31.233334 };
const zoom = 5;
useEffect(() => {
if (ref.current && !map) {
setMap(new window.google.maps.Map(ref.current, {}));
}
if (map) {
["click", "idle"].forEach((eventName) =>
google.maps.event.clearListeners(map, eventName)
);
if (onClick) {
map.addListener("click", onClick);
}
if (onIdle) {
map.addListener("idle", () => onIdle(map));
}
}
}, [ref, map, onClick, onIdle]);
return(
<div className='App'>
<Wrapper apiKey={API_KEY} libraries={['places', 'drawing', 'geometry']}>
<Map
center={center}
onClick={onMapClick}
onIdle={onIdle}
zoom={zoom}
style={{ flexGrow: "1", height: '600px' }}
/>
</Wrapper>
</div>
)
}
export default App;
after giving the inner div the proper height and width the map element appears inside inner div but the element itself isn't showing, although all click handlers related to the map are working. Can any anyone help me to determine where's the problem?
We upgraded MUI from v4 to v5 and we have UI tests which started failing. Error is:
TypeError: Cannot read property 'secondary' of undefined (I added comment to which line in code this refers)
Test example:
describe('<AnonDragNDropFileUpload />', () => {
it('should render', () => {
const blob = () => {
return new File(['Test string'], 'Test file.txt');
};
const fileSet: AnonFileSet = {
originalFile: { get: blob, set: () => undefined },
compressedFile: { get: () => undefined, set: () => undefined },
};
const result = render(<AnonDragNDropFileUpload fileSet={fileSet} downloadFileAction={jest.fn()} clearFileAction={jest.fn()} />);
expect(result).toBeTruthy();
});
});
Code:
import { Paper } from '#mui/material';
import { green, red } from '#mui/material/colors';
import { lighten, Theme } from '#mui/material/styles';
import makeStyles from '#mui/styles/makeStyles';
import { JobInputFileTypeEnum } from 'app/api';
import React, { useCallback, useEffect, useState } from 'react';
import { useDropzone } from 'react-dropzone';
import { AnonFileSet } from '.';
const useDropZoneStyles = makeStyles((theme: Theme) => ({
dragndropZone: {
backgroundColor: lighten(theme.palette.secondary.light, 0.8), // <-- this line fails
width: '100%',
},
info: {
backgroundColor: green[100],
width: '100%',
},
}));
interface Props {
fileSet: AnonFileSet;
clearFileAction: (fileType?: JobInputFileTypeEnum) => void;
downloadFileAction: () => void;
}
export const AnonDragNDropFileUpload: React.FC<Props> = ({ fileSet, clearFileAction, downloadFileAction }) => {
const classes = useDropZoneStyles();
const [fileLabel, setFileLabel] = useState('');
const onDrop = useCallback(async (acceptedFiles: File[]) => {
setFileLabel(fileSet.originalFile.get()?.name ?? '');
fileSet.originalFile.set(acceptedFiles[0]);
}, []);
const { acceptedFiles, getRootProps, getInputProps } = useDropzone({ onDrop, multiple: false, accept: '.csv' });
const { ref, ...rootProps } = getRootProps();
const handleDeleteFile = () => {
acceptedFiles.splice(
acceptedFiles.findIndex((x) => x.name === fileSet.originalFile.get()?.name),
1,
);
clearFileAction();
};
useEffect(() => {
setFileLabel(fileSet.originalFile.get()?.name ?? '');
}, [fileSet.originalFile.get()]);
if (fileSet.originalFile.get())
return (
<Paper variant="outlined">
<div className="flex px-8 py-32 justify-center">
<div className="flex">
<a style={{ color: '#888888', textDecoration: 'underline', cursor: 'default' }}>{fileLabel}</a>
<p className="mx-4"> </p>
<a onClick={handleDeleteFile} style={{ color: red[600], cursor: 'pointer' }} role="link">
{'[Clear File]'}
</a>
<p className="mx-4"> </p>
{fileSet.compressedFile?.get() && (
<a onClick={downloadFileAction} style={{ color: green[600], cursor: 'pointer' }} role="link">
{'[Download File]'}
</a>
)}
</div>
</div>
</Paper>
);
return (
<Paper {...rootProps} className={classes.dragndropZone} variant="outlined">
<div className="flex px-8 py-32 justify-center">
<input {...getInputProps()} name="customerCSVFilename" placeholder="CSV File"/>
<p>{fileLabel}</p>
</div>
</Paper>
);
};
What I've tried so far:
Checked if ThemeProvider is available
Added custom theme just to the code block which fails
All other tests which are testing hooks or custom logic (like pure TypeScript) are working without any issues, but it seems that somehow using styles from MUI is not working. When I remove these lines, test is passing, so my guess it has something with MUI makeStyles.
Any ideas? Thanks for helping me out.
Try to use a mocked component, wrapped inside a ThemeProvider instance:
import theme from './path/to/your/theme'
const MockAnonDragNDropFileUpload = (props: any) => {
return (
<ThemeProvider theme={theme}>
<AnonDragNDropFileUpload {...props} />
</ThemeProvider>
);
}
To mock the component using the existing theme you could separate its declaration into a distinct file:
const theme = createTheme({
...
});
export default theme;
Then use the mocked instance in the tests:
describe('<AnonDragNDropFileUpload />', () => {
it('should render', () => {
...
const result = render(
<MockAnonDragNDropFileUpload
fileSet={fileSet}
downloadFileAction={jest.fn()}
clearFileAction={jest.fn()}
/>
);
expect(result).toBeTruthy();
});
});
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)} />);
};
Hi I have the following React component that positions its children with styles.
const styles = () => ({
bathButt : {
top :278,
left : 336
},
})
class AudioZones extends Component {
render() {
const { classes } = this.props;
return (
<IconButton className={classes.bathButt} >
<Speaker/>
</IconButton>
);
}
}
export default withStyles(styles) (AudioZones);
I have created a child component "AudioZone"
render()
return (
);
}
which i substitute into the parent
render() {
const { classes } = this.props;
return (
<AudioZone/> );
}
However I have run into trouble on how I pass down the "bathButt" style so that the position of the button is set in the parent but read and rendered by the child.
Any help appreciated
For withStyles you can use Higher-Order Components (HOC) to pass styles from parent to child
const styles = () => ({
bathButt: {
top: 20,
left: 30,
backgroundColor: "blue"
}
});
const withMyStyles = WrappedComponent => {
const WithStyles = ({ classes }) => {
return (
<div>
<WrappedComponent classes={classes} />
</div>
);
};
return withStyles(styles)(WithStyles);
};
and use it in your child component
class AudioZones extends Component {
render() {
const { classes } = this.props;
return (
<IconButton className={classes.bathButt}>
<h1>Speaker Component</h1>
</IconButton>
);
}
}
export default withMyStyles(AudioZones);
but insted of withStyles you can use makeStyles,i think its easer
const useStyles = makeStyles({
bathButt: { top: 20, left: 50, color: "red" } // a style rule
});
function App(props) {
return <AudioZones useStyles={useStyles} />;
}
child component
function AudioZones(props) {
const classes = useStyles();
return (
<div>
<IconButton className={classes.bathButt}>
<h1>Speaker Component</h1>
</IconButton>
</div>
);
}
Working Codesandbox for withStyles and makeStyles
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>
);
}
}