Cannot get input field to focus on click - javascript

So i am currently unable to focus on an input field - I have a card, which has a onClick handler. Once you clicked it - it brings up the input field, however, i cannot get the input field to focus:
The key area where things are going wrong are within renderTextField where i have set a reference, and then call this.myInput.focus() in handleClickInside, but still no luck.
Code below
import React, { Component } from 'react';
import { CSSTransition, transit } from 'react-css-transition';
import Text from './Text';
import Card from './Card';
import MenuIcon from '../icons/MenuIcon';
const style = {
container: { height: '60px', width: '300px' },
input: { border: 'none', fontSize: '14px', background: '#ff000000' },
icon: { position: 'absolute', right: '10px', top: '18px' },
};
class Input extends Component {
state = {
clickedOutside: true,
value: '',
};
componentDidMount() {
document.addEventListener('mousedown', this.handleClickOutside);
}
componentWillUnmount() {
document.removeEventListener('mousedown', this.handleClickOutside);
}
myRef = React.createRef();
myInput = React.createRef();
handleClickOutside = (e) => {
if (!this.myRef.current.contains(e.target)) {
this.setState({ clickedOutside: true });
}
};
handleClickInside = () => {
this.setState({ clickedOutside: false });
this.myInput.current.focus();
}
renderField = () => {
const { clickedOutside, value } = this.state;
return (
<div>
<CSSTransition
defaultStyle={{ transform: 'translate(0, -2px)' }}
enterStyle={{ transform: transit('translate(0, -16px)', 150, 'ease-in-out') }}
leaveStyle={{ transform: transit('translate(0, -2px)', 150, 'ease-in-out') }}
activeStyle={{ transform: 'translate(0, -16px)' }}
active={!clickedOutside || value.length > 0}
>
<Text>Password</Text>
{ this.renderTextField() }
</CSSTransition>
</div>
);
}
renderTextField = () => {
const { clickedOutside, value } = this.state;
return (
<input
ref={this.myInput}
style={style.input}
type="text"
value={value}
placeholder={!clickedOutside ? 'Enter Password...' : null}
disabled={clickedOutside}
onChange={e => this.setState({ value: e.target.value })}
/>
);
}
renderIcon = () => (
<div style={style.icon}>
<MenuIcon />
</div>
)
render() {
return (
<div ref={this.myRef} onClick={this.handleClickInside} >
<Card override={style.container}>
{ this.renderField() }
{ this.renderIcon() }
</Card>
</div>
);
}
}
export default Input;

First create a ref:
myInput = React.createRef();
Assign the ref:
ref={this.myInput}
Then, to focus myInput, use:
this.myInput.current.focus()

Related

React when change state from child, the parent reset the state

I have the component in the snippet below
import { FC, useState, useEffect, createContext, useContext } from 'react';
import { Button } from '#swvl/button';
type option = {
name: string;
icon?: JSX.Element;
};
type SwitchContentContextType = {
options: option[];
setOptions: (option: option[]) => void;
};
type SwitchContentCompoundTypes = {
Content: FC<option>;
};
const SwitchContentContext = createContext({} as SwitchContentContextType);
export const SwitchContent: FC & SwitchContentCompoundTypes = ({ children }) => {
const [options, setOptions] = useState<option[]>([]);
useEffect(() => {
console.log('options', options);
}, [options]);
return (
<SwitchContentContext.Provider value={{ options, setOptions }}>
<div
sx={{
backgroundColor: 'secondary-light-90',
display: 'inline-flex',
borderRadius: '20px',
border: '1px solid transparent',
}}
>
{options.map((option, index) => {
return (
<Button
key={index}
sx={{
backgroundColor: 'secondary-light-90',
borderRadius: 'inherit',
color: 'content-quarternary',
'&:hover, &:active': {
bg: 'secondary',
borderRadius: '20px',
color: 'primary-light-100',
},
'& span': { variant: 'text.p-small' },
}}
>
{option.name}
</Button>
);
})}
</div>
{children}
</SwitchContentContext.Provider>
);
};
const Content: SwitchContentCompoundTypes['Content'] = ({ children, name, icon, ...rest }) => {
const { options, setOptions } = useContext(SwitchContentContext);
useEffect(() => {
const optionsCopy = [...options];
optionsCopy.push({ name, icon });
setOptions(optionsCopy);
}, []);
return <div {...rest}>{children}</div>;
};
SwitchContent.Content = Content;
when try to push a new option top options state it should has 2 members but it only has the last one. it seems that change the options re-render the parent and this cause to redefine the state, but what I know is re-render will not reset the state so I need to know how to cause and how to fix this
below is how I use the component
<SwitchContent>
<SwitchContent.Content name="Regular mode">
<p>Content 1</p>
</SwitchContent.Content>
<SwitchContent.Content name="B2B mode">
<p>Content 2</p>
</SwitchContent.Content>
</SwitchContent>
I try to console log the options value in useEffect in the parent component like below
useEffect(() => {
console.log('options', options);
}, [options]);
and got below result

How to change prop of Material UIs Button component based on screen size breakpoint when using a class component

Im using Material UI's Button component and would like to conditionally determine the variant of the button. That is, on screens medium and up, the variant should be 'outlined' and when the screen is smaller than medium, the variant type should be none. I am using a class component. I have done my research to see what others have done. I have seen the method of using useMediaQuery method but that would require me to change the component to a functional component. Im not sure if I know if that is a good idea because the component is rather a large one and that means will be a bit confusing to convert. I also tried using a ternary operator like this:
const variantType = theme.breakpoints.down("md") ? '' : 'outline';
<Button variant={variantType}>
Food Pick
</Button>
But this didnt do the job. Is there any other way to accomplish this?
Update: Here is my code after trying to wrap the component but in a functional component but its giving an error:
import { withStyles, withTheme, useTheme } from '#material-ui/core';
import PropTypes from 'prop-types';
import Button from '#material-ui/core/Button';
import Typography from '#material-ui/core/Typography';
import Grid from '#material-ui/core/Grid';
import { PulseLoader } from 'react-spinners';
import Icon from '#material-ui/core/Icon';
import Chip from '#material-ui/core/Chip';
import useMediaQuery from '#material-ui/core/useMediaQuery';
const styles = theme => ({
beta: {
height: '17px',
fontSize: '12px',
marginLeft: '10px'
},
scrollContainer: {
flex: 1,
maxHeight: '400px',
minHeight: '300px',
overflowY: 'auto'
},
progressContainer: {
height: '350px',
},
modalDescription: {
color: theme.palette.text.secondary,
marginTop: '20px',
},
button: {
marginTop: '20px',
marginLeft: '10px',
marginRight: '10px',
},
smartSuggestContainer: {
textAlign: 'right',
paddingRight: '35px',
[theme.breakpoints.down('xs')]: {
display: 'flex',
justifyContent: 'center',
flexDirection: 'row',
margin: '40px 0'
},
},
});
export default function MyComponentWrapper({ ...rest }) {
const theme = useTheme();
const mediumScreen = useMediaQuery(theme.breakpoints.down('md'));
return <FoodDescriptions {...rest} mediumScreen={mediumScreen} />;
}
class FoodDescriptions extends Component {
static PAGE_SIZE = 25;
constructor(props) {
super(props);
this.state = {
showFoodDescriptionsModal: false,
dataLoaded: false,
data: [],
page: 0,
sortBy: 'return',
sortDir: 'desc',
};
}
componentDidMount() {
document.addEventListener('keydown', this.handleKeypress);
}
componentWillUnmount() {
document.removeEventListener('keydown', this.handleKeypress);
}
fetchData = async (page, sortBy, sortDir) => {
const { currentBotId } = this.props;
const offset = page * FoodDescriptions.PAGE_SIZE;
const data = await getFoodDescriptions(currentBotId, sortBy, sortDir, FoodDescriptions.PAGE_SIZE, offset);
this.setState({
dataLoaded: true,
data,
});
};
handleKeypress = (e) => {
const { showSnartConfigsModal } = this.state;
const { key } = e;
if (key === 'Escape' && showSnartConfigsModal) {
this.closeModal();
}
};
applyConfig = (botId, params) => {
const { updateConfig, botConfig, actions } = this.props;
updateConfig({ name: botConfig.name, config: Object.assign(botConfig.config, params) });
this.closeModal();
actions.showNotification({ data: 'Configuration has been applied' });
};
openModal = () => {
const { page, sortBy, sortDir } = this.state;
this.fetchData(page, sortBy, sortDir);
this.setState({
showFoodDescriptionsModal: true,
});
};
closeModal = () => {
this.setState({
showFoodDescriptionsModal: false,
});
};
changePage = (page) => {
const { sortBy, sortDir } = this.state;
this.setState({
page,
dataLoaded: false
}, () => this.fetchData(page, sortBy, sortDir));
};
changeSortBy = (sortBy) => {
/* eslint-disable-next-line prefer-destructuring */
let sortDir = this.state.sortDir;
if (sortBy === this.state.sortBy) {
sortDir = (this.state.sortDir === 'desc') ? 'asc' : 'desc';
}
this.setState({
sortBy,
sortDir,
dataLoaded: false,
}, () => this.fetchData(this.state.page, sortBy, sortDir));
};
renderEmptyState() {
const { classes } = this.props;
return (
<Grid container alignItems="center" justify="center" className={classes.progressContainer}>
<Typography className={classes.noCoinsText}>No configurations found</Typography>
</Grid>
);
}
renderLoader() {
const { classes } = this.props;
return (
<Grid container alignItems="center" justify="center" className={classes.progressContainer}>
<PulseLoader size={6} color="#52B0B0" loading />
</Grid>
);
}
renderTable() {
const { data, page } = this.state;
return (
<StrategiesTable
strategies={data}
onClickCopy={this.applyConfig}
page={page}
changePage={this.changePage}
sortBy={this.changeSortBy} />
);
}
render() {
const {
classes, userFeatures, botConfig, theme
} = this.props;
const { showFoodDescriptionsModal, dataLoaded } = this.state;
if (!botConfig) {
return null;
}
return (
<Fragment>
<div className={classes.smartSuggestContainer}>
<Button
name="discoverConfigs"
variant={theme.breakpoints.down(600) ? '' : 'outlined'}
color="primary"
size="small"
disabled={!userFeatures['smart_suggest_backtests'.toUpperCase()] || botConfig.status.toLowerCase() === STATUSES.RUNNING}
onClick={this.openModal}>
Food Buy
</Button>
</div>
</Fragment>
);
}
}
function mapStateToProps(state) {
return {
userFeatures: state.global.paywall.features,
};
}
function mapDispatchToProps(dispatcher) {
return {
actions: {
...bindActionCreators({
showNotification,
}, dispatcher)
}
};
}
connect(mapStateToProps, mapDispatchToProps)(withTheme()(withStyles(styles)(FoodDescriptions)));
Why don't you just create a functional component using useMediaQuery and returning the responsive Button ?

How can I make the Snackbar work in a class component?

These are my codes for the snackbar and it wasn't working whenever I'll click the button. I wanted the snackbar to appear once I'll click the button "confirm". Almost all of the examples I have seen are in a functional component, so how can I make the Snackbar work as expected in a class component?
class name extends Component {
constructor() {
super();
this.state = { orders: [], open: false };
}
handleOpen = () => this.setState({ open: true });
handleClose = () => this.setState({ open: false });
columns = [
{
name: "Confirm",
options: {
customBodyRender: (value, tableMeta) => {
return (
<FormControlLabel
value={value}
control={
<Button>
confirm
</Button>
}
onClick={(e) => {
try {
//firestore codes
);
} catch (err) {
console.log(err);
}
this.handleOpen();
}}
/>
);
},
},
},
];
//code for options
//data fetching codes
render() {
const { open } = this.state;
return this.state.orders ? (
<div>
//muidatatable codes
<Snackbar
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
open={open}
onClose={this.handleClose}
autoHideDuration={2000}
// other Snackbar props
>
Order Confirmed
</Snackbar>
</div>
) : (
<p>Loading...</p>
);
}
}
Ignoring a few syntax errors, you should check if there are any orders by using the length and not just by mere existence of the array as you have initialized an empty array this.state.orders will always result in true. Instead use this.state.orders.length > 0 ? to check if there are any orders or not.
Snackbar's child(ren) should be wrapped in components and not just strings directly, for using string directly you can use message prop of Snackbar.
Also, it's a standard to write class's name starting with an upper-case letter.
Here's a working code: Material UI Snackbar using classes
import React, { Component } from "react";
import { FormControlLabel, Button, Snackbar } from "#material-ui/core";
import MuiAlert from "#material-ui/lab/Alert";
export default class Name extends Component {
constructor() {
super();
this.state = { orders: [], open: false };
}
handleOpen = () => this.setState({ open: true });
handleClose = () => this.setState({ open: false });
handleClick = () => this.setState({ orders: [1], open: true });
columns = [
{
name: "Confirm",
options: {
customBodyRender: (value, tableMeta) => {
return (
<FormControlLabel
value={value}
control={<Button>confirm</Button>}
onClick={(e) => {
try {
//firestore codes
} catch (err) {
console.log(err);
}
this.handleOpen();
}}
/>
);
}
}
}
];
//code for options
//data fetching codes
render() {
const { open } = this.state;
return (
<>
<Button variant="outlined" onClick={this.handleClick}>
Open snackbar
</Button>
{this.state.orders.length > 0 ? (
<div>
<Snackbar
anchorOrigin={{
vertical: "bottom",
horizontal: "left"
}}
open={open}
onClose={this.handleClose}
autoHideDuration={2000}
// other Snackbar props
>
{/* <span
style={{
background: "#000",
color: "#fff",
padding: "20px 5px",
width: "100%",
borderRadius: "5px"
}}
>
Order Confirmed
</span> */}
<MuiAlert
onClose={this.handleClose}
severity="success"
elevation={6}
variant="filled"
>
Success Message
</MuiAlert>
</Snackbar>
</div>
) : (
<p>loading...</p>
)}
</>
);
}
}
The following changes are made to make it work:
Removed Order Confirmed and used message prop of Snackbar
Passed values to orders array in constructor
Passed true in open variable.
Below is the working code for snack bar.
import React, { Component } from "react";
import Snackbar from "#material-ui/core/Snackbar";
class SnackBarSof extends Component {
constructor() {
super();
this.state = { orders: [1, 2], open: true };
}
handleOpen = () => this.setState({ open: true });
handleClose = () => this.setState({ open: false });
render() {
console.log(this.state.orders);
console.log(this.state);
const { open } = this.state;
return this.state.orders ? (
<div>
<Snackbar
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
open={open}
onClose={this.handleClose}
message="order confirmed"
autoHideDuration={2000}
></Snackbar>
</div>
) : (
<p>Loading...</p>
);
}
}
export default SnackBarSof;

Rendering multiple input fields causing lag after 200 items

I have a project where i can have n-number of input field. After when i add 200 items, it starts lagging after that. Demo : https://testcreate.vivekneel.now.sh/create (To test: Focus last input and try pressing enter button to create new input)
Source Code. : https://github.com/VivekNeel/Create-Test
This is my main container :
import React, { useState } from "react";
import CreateTerms from "./CreateTerms";
import { initTerms, contructTermObject } from "./utils";
import { Container } from "#material-ui/core/";
const CreateStudySetContainer = () => {
const [terms, setTerms] = useState(initTerms);
const [inputIdToFocus, setInputIdToFocus] = useState(null);
const handleCreateTerm = () => {
const newTerm = contructTermObject(terms.length + 1, 2);
const newTerms = [...terms, newTerm];
setInputIdToFocus(terms.length + 1);
setTerms(newTerms);
};
console.log("....rendering");
return (
<Container maxWidth={"md"}>
<CreateTerms
terms={terms}
inputIdToFocus={inputIdToFocus}
createTerm={handleCreateTerm}
/>
;
</Container>
);
};
export default CreateStudySetContainer;
This the CreateTerms code :
import React from "react";
import CreateFacts from "./CreateFacts";
import { withStyles, Card } from "#material-ui/core/";
import ContentItemRow from "./ContentItemRow";
const styles = () => ({
card: {
marginBottom: 16,
},
});
const CreateTerms = (props) => {
const { terms, classes, createTerm, inputIdToFocus } = props;
return (
<div className={classes.container}>
{terms.map(({ node: { term } }, index) => {
return (
<Card key={term.id} className={classes.card}>
<p>{index}</p>
<ContentItemRow
autoFocus={term.id === inputIdToFocus}
createTerm={createTerm}
term={term}
/>
;
</Card>
);
})}
</div>
);
};
export default withStyles(styles)(CreateTerms);
This is ContentItemRow :
import React from "react";
import CreateFacts from "./CreateFacts";
import { withStyles } from "#material-ui/core/";
import ContentEditor from "./ContentEditor";
const styles = {
container: {
display: "flex",
flexDirection: "row",
alignItems: "center",
justifyContent: "flex-start",
},
term: {
marginRight: 16,
flex: 1,
},
facts: {
flex: 1,
},
};
const ContentItemRow = (props) => {
const { term, classes, createTerm, autoFocus } = props;
return (
<div className={classes.container}>
<div className={classes.term}>
<ContentEditor
autoFocus={autoFocus}
createTerm={createTerm}
placeholder={"New term"}
/>
</div>
<div className={classes.facts}>
{term.facts.map(({ fact }) => {
return (
<ContentEditor
key={fact.id}
createTerm={createTerm}
placeholder={"New fact"}
/>
);
})}
</div>
</div>
);
};
export default withStyles(styles)(ContentItemRow);
This is ContentEditor :
import React from "react";
import { TextField } from "#material-ui/core/";
const ContentEditor = (props) => {
const { placeholder, createTerm, autoFocus } = props;
const handleOnKeyDown = (event) => {
const { keyCode } = event;
if (keyCode === 13) {
createTerm();
}
};
return (
<TextField
onKeyDown={handleOnKeyDown}
fullWidth
autoFocus={autoFocus}
placeholder={placeholder}
/>
);
};
export default ContentEditor;
When debugging, I noticed that dom updates only the last div which gets added. I don't know where the lagging is coming from.
Not sure if this will work but you can try the following:
const ContentItemRow = React.memo(function ContentItemRow (props) => {
And to prevent re creating the create term handler:
const handleCreateTerm = useCallback(() => {
setTerms((terms) => {
const newTerm = contructTermObject(
terms.length + 1,
2
);
const newTerms = [...terms, newTerm];
setInputIdToFocus(terms.length + 1);
return newTerms;
});
}, []);

How to scan one barcode per time? [react-native-camera]

Actually i'm new to React and i'm trying to make a simple barcode scanner which show the scanned barcode in an alert and after pressing "OK" in the alert the user should be able to scan another barcode.
The issue is that the barcode is continuously scanned and when the alert is up it's hiding and showing every second the alert.
I was trying to do something like this to show the alert only once and if OK is pressed to be able to show again the alert but only in case the OK is pressed but that had no effect..
onBarCodeRead = (e) => {
if(!this.alertPresent){
this.alertPresent = true;
Alert.alert(
"Barcode type is " + e.type,
"Barcode value is " + e.data,
[
{text: 'OK', onPress: () => this.alertPresent = false;},
],
{cancelable: false},
);
}
}
Here is full code of Barcode.JS
import React, { Component } from 'react';
import { Button, Text, View,Alert } from 'react-native';
import { RNCamera } from 'react-native-camera';
import BarcodeMask from 'react-native-barcode-mask';
class ProductScanRNCamera extends Component {
constructor(props) {
super(props);
this.camera = null;
this.barcodeCodes = [];
this.alertPresent = false;
this.state = {
camera: {
flashMode: RNCamera.Constants.FlashMode.auto,
}
};
}
onBarCodeRead = (e) => {
if(!this.alertPresent){
this.alertPresent = true;
Alert.alert(
"Barcode type is " + e.type,
"Barcode value is " + e.data,
[
{text: 'OK', onPress: () => this.alertPresent = false;},
],
{cancelable: false},
);
}
}
pendingView() {
return (
<View
style={{
flex: 1,
backgroundColor: 'lightgreen',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text>Waiting</Text>
</View>
);
}
render() {
return (
<View style={styles.container}>
<RNCamera
ref={ref => {
this.camera = ref;
}}
defaultTouchToFocus
flashMode={this.state.camera.flashMode}
mirrorImage={false}
onBarCodeRead={this.onBarCodeRead.bind(this)}
onFocusChanged={() => {}}
onZoomChanged={() => {}}
style={styles.preview}
>
<BarcodeMask/>
</RNCamera>
</View>
);
}
}
The trick here is to modify barcodeTypes props with an internal state.
const defaultBarcodeTypes = [// should be all Types from RNCamera.Constants.BarCodeType];
class ProductScanRNCamera extends Component {
state = {
// your other states
barcodeType: '',
barcodeValue: '',
isBarcodeRead: false // default to false
}
onBarcodeRead(event) {
this.setState({isBarcodeRead: true, barcodeType: event.type, barcodeValue: event.data});
}
// run CDU life-cycle hook and check isBarcodeRead state
// Alert is a side-effect and should be handled as such.
componentDidUpdate() {
const {isBarcodeRead, barcodeType, barcodeValue} = this.state;
if (isBarcodeRead) {
Alert.alert(barcodeType, barcodeValue, [
{
text: 'OK',
onPress: () => {
// Reset everything
this.setState({isBarcodeRead: false, barcodeType: '', barcodeValue: ''})
}
}
]);
}
}
render() {
const {isBarcodeRead} = this.state;
return (
<RNCamera {...your other props} barcodeTypes={isBarcodeRead ? [] : defaultBarcodeTypes}>
<BarcodeMask />
</RNCamera>
)
}
}
A hook version is cleaner
const ProductScanRNCamera = () => {
const [isBarcodeRead, setIsBarcodeRead] = useState(false);
const [barcodeType, setBarcodeType] = useState('');
const [barcodeValue, setBarcodeValue] = useState('');
useEffect(() => {
if (isBarcodeRead) {
Alert.alert(
barcodeType,
barcodeValue,
[
{
text: 'OK',
onPress: () => {
// reset everything
setIsBarcodeRead(false);
setBarcodeType('');
setBarcodeValue('');
}
}
]
);
}
}, [isBarcodeRead, barcodeType, barcodeValue]);
const onBarcodeRead = event => {
if (!isBarcodeRead) {
setIsBarcodeRead(true);
setBarcodeType(event.type);
setBarcodeValue(event.data);
}
}
return (
<RNCamera {...your props}
onBarCodeRead={onBarcodeRead}
barcodeTypes={isBarcodeRead ? [] : defaultBarcodeTypes}>
<BarcodeMask />
</RNCamera>
)
}
use setState in order to set state of component.setState will take the object and update the state of component
check code below
import React, { Component } from 'react';
import { Button, Text, View, Alert } from 'react-native';
import { RNCamera } from 'react-native-camera';
import BarcodeMask from 'react-native-barcode-mask';
class ProductScanRNCamera extends Component {
constructor(props) {
super(props);
this.camera = null;
this.barcodeCodes = [];
this.showAlert = true;
this.state = {
camera: {
flashMode: RNCamera.Constants.FlashMode.auto,
}
};
}
onBarCodeRead = (e) => {
if (this.state.alertPresent) {
this.setState({ showAlert: false });
Alert.alert(
"Barcode type is " + e.type,
"Barcode value is " + e.data,
[
{ text: 'OK', onPress: () =>console.log('ok') },
],
{ cancelable: false },
);
}
}
pendingView() {
return (
<View
style={{
flex: 1,
backgroundColor: 'lightgreen',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text>Waiting</Text>
</View>
);
}
render() {
return (
<View style={styles.container}>
<RNCamera
ref={ref => {
this.camera = ref;
}}
defaultTouchToFocus
flashMode={this.state.camera.flashMode}
mirrorImage={false}
onBarCodeRead={this.onBarCodeRead.bind(this)}
onFocusChanged={() => { }}
onZoomChanged={() => { }}
style={styles.preview}
>
<BarcodeMask />
</RNCamera>
</View>
);
}
}

Categories

Resources