Im tying to show up a pop up whit the following code :
const [showPopup, setShowPopup] = useState(false);
Im handling the show/setpopup by this way :
<Popup open={showPopup} onClose={() => setShowPopup(false)} modal>
<span> Popup content </span>
</Popup>
{meta.error === 'codigo 2 fatores incorreto' ? (
setShowPopup(true)
) : (
<Popup style={{ visibility: "hidden" }}>.</Popup>
)}
When it drops in the case (meta.error === 'codigo 2 fatores incorreto') he drops in a loop with the following eror : (Too many re-renders. React limits the number of renders to prevent an infinite loop. ) , someone knows how to solute it ?
I used this doc https://react-popup.elazizi.com/component-api/
whole component [WORKING] :
import React, { useState, useEffect } from 'react';
import { ErrorMessage, useField } from "formik";
import { StyledTextInput, StyledLabel, StyledIcon, ErrorMsg } from "./Styles";
// Eye for password
import { FiEyeOff, FiEye } from "react-icons/fi";
//pop up style.css
import '../assets/css/popup.css'
// Import popup lib
import Popup from "reactjs-popup";
import 'reactjs-popup/dist/index.css';
function MyComponent() {
const [state, setState] = useState();
setState(true);
return (
<Popup model
trigger={open => <MyComponent open={open} />}
position="right center"
closeOnDocumentClick
>
<span> Popup content </span> </Popup>
);
}
export const TextInput = ({ icon, ...props }) => {
const [field, meta] = useField(props);
const [showpass, setShowpass] = useState(false);
const [showPopup, setShowPopup] = useState(false);
useEffect(() => {
if(meta.error === 'codigo 2 fatores incorreto'){
setShowPopup(true);
}
}, [meta.error])
return (
<div style={{ position: "relative" }}>
<StyledLabel htmlFor={props.name}>{props.label}</StyledLabel>
{props.type !== "password" && (
<StyledTextInput
invalid={meta.touched && meta.error}
{...field}
{...props}
/>
)}
{props.type === "password" && (
<StyledTextInput
invalid={meta.touched && meta.error}
{...field}
{...props}
type={showpass ? "text" : "password"}
/>
)}
<StyledIcon>{icon}</StyledIcon>
{props.type === "password" && (
<StyledIcon onClick={() => setShowpass(!showpass)} right>
{showpass && <FiEye />}
{!showpass && <FiEyeOff />}
</StyledIcon>
)}
{meta.touched && meta.error ? (
<ErrorMsg>{meta.error}</ErrorMsg>
) : (
<ErrorMsg style={{ visibility: "hidden" }}>.</ErrorMsg>
)}
<Popup open={showPopup} onClose={() => setShowPopup(false)} modal>
{close => (
<div className="modal">
<button className="close" onClick={close}>
×
</button>
</div>
)}
</Popup>
{meta.error === "codigo 2 fatores incorreto" ? (
!showPopup ? ( setShowPopup(true)) : ("") // <-- Only set state if not true
) : <Popup>.</Popup>}
</div>
);
};
We should never ever use a setState inside the components render method. For class components, that is inside the render(), for function components, that is anywhere inside return() or in the component body, like here:
function MyComponent() {
const [state, setState] = useState();
setState(true);
return (...);
}
This will always cause an infinite loop.
setState() triggers re-render.
Re-render runs the component code again and triggers setState(). Go back to 1.
React provides tools to handle your case, such as useEffect.
Instead of
{meta.error === "codigo 2 fatores incorreto" ? (
setShowPopup(true)
) : (
<Popup style={{ visibility: "hidden" }}></Popup>
)}
You should have
export const TextInput = ({ icon, ...props }) => {
...
useEffect(() => {
if(meta.error){
setShowPopup(true);
}
}, [meta.error])
return (
...
<Popup style={{visibility: "hidden"}}>.</Popup>
);
If I'm reading this right, it seems whenever meta.error matches your string, it'll constantly call setShowPopup(true) because the state updated - and calling that function causes the re-render, during which I assume meta.error is still 'codigo 2 fatores incorreto'.
I believe you could do something like the following to stop the re-rendering.
{meta.error === "codigo 2 fatores incorreto" ? (
!showPopup ? setShowPopup(true) : "" // <-- Only set state if not true
) : (
<Popup style={{visibility: "hidden"}}>.</Popup>
)}
I may be wrong though, and I may be misunderstanding the snippet.
Related
I am using the react-native-confirmation-code-field package. I want the keyboard to be showing as soon as the screen renders and the first cell in focus. Any ideas how to do this?
Edit: Including my code below:
export default function ConfirmationCode({ route, navigation }) {
const [value, setValue] = useState("")
const ref = useBlurOnFulfill({ value, cellCount: CELL_COUNT })
const [props, getCellOnLayoutHandler] = useClearByFocusCell({value, setValue})
return (
<CodeField
ref={ref}
{...props}
value={value}
onChangeText={setValue}
cellCount={CELL_COUNT}
rootStyle={styles.codeFieldRoot}
keyboardType="number-pad"
textContentType="oneTimeCode"
renderCell={({ index, symbol, isFocused }) => (
<Text
key={index}
style={[styles.cell, isFocused && styles.focusCell]}>
{symbol || (isFocused ? <Cursor /> : null)}
</Text>
)
}
/>
)
}
Change Text component to TextInput. But make sure it is disabled.
Set a ref(you would need the first one).
And on useEffect, call the focus method on that ref.
Roughly, it should look something like this:
export default function ConfirmationCode({ route, navigation }) {
const [value, setValue] = useState("")
const ref = useBlurOnFulfill({ value, cellCount: CELL_COUNT })
const [props, getCellOnLayoutHandler] = useClearByFocusCell({value, setValue})
const textInputRef = useRef(null);
useEffect(() => {
textInputRef.current?.focus()
}, []);
return (
<CodeField
ref={ref}
{...props}
value={value}
onChangeText={setValue}
cellCount={CELL_COUNT}
rootStyle={styles.codeFieldRoot}
keyboardType="number-pad"
textContentType="oneTimeCode"
renderCell={({ index, symbol, isFocused }) => (
<TextInput
key={index}
ref={index === 0 && textInputRef)
style={[styles.cell, isFocused && styles.focusCell]}>
{symbol || (isFocused ? <Cursor /> : null)}
</TextInput>
)
}
/>
)
}
So it turns out that CodeField does have an autoFocus property, my bad. The solution is simply to add autoFocus={true} as a prop to the CodeField component:
<CodeField
ref={ref}
{...props}
autoFocus={true}
value={value}
onChangeText={setValue}
cellCount={CELL_COUNT}
rootStyle={styles.codeFieldRoot}
keyboardType="number-pad"
textContentType="oneTimeCode"
renderCell={({ index, symbol, isFocused }) => (
<TextInput
key={index}
ref={index === 0 && textInputRef)
style={[styles.cell, isFocused && styles.focusCell]}>
{symbol || (isFocused ? <Cursor /> : null)}
</TextInput>
)
}
/>
I have a form input component where I am checking if the input is active to set the active class to the input's label:
import React, { forwardRef, ReactElement, ReactNode, HTMLProps, useImperativeHandle } from 'react'
import styles from './index.module.css'
import classNames from 'classnames'
import { IconFa } from '../icon-fa'
import { useStableId } from '../use-stable-id'
export interface Props extends HTMLProps<HTMLInputElement> {
// An icon component to show on the left.
icon?: ReactNode
// A help text tooltip to show on the right.
help?: string
// A clear text tooltip to show on the right.
clear?: string
// Pass an onHelp click handler to show a help button.
onHelp?: () => void
// Pass an onClear click handler to show a clear button.
onClear?: () => void
errorMessage?: ReactNode
label?: string
hideLabel?: boolean
}
export const FormInput = forwardRef<HTMLInputElement, Props>(function FormInput(props, ref): ReactElement {
const { id, icon, help, hideLabel, errorMessage, onHelp, onClear, ...rest } = props
const internalRef = React.useRef<HTMLInputElement>(null)
useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(ref, () => internalRef.current)
const stableId = useStableId()
const inputId = id ? id : stableId
const active = document.activeElement === internalRef.current
const inputClassName = classNames(
styles.input,
icon && styles.hasLeftIcon,
(help || onHelp || onClear) && styles.hasRightIcon
)
const labelClassName = classNames(
styles.label,
Boolean(props.value) && styles.hasValue,
props['aria-invalid'] && styles.hasError,
active && styles.active,
props.className
)
return (
<div className={labelClassName}>
{!hideLabel && (
<label className={styles.labelText} htmlFor={inputId}>
{props.label}
{props.required && '*'}
</label>
)}
<div className="relative">
{icon && <div className={styles.leftIcon}>{icon}</div>}
<input
{...rest}
id={inputId}
ref={internalRef}
aria-invalid={props['aria-invalid']}
aria-label={props.hideLabel ? props.label : undefined}
className={inputClassName}
/>
{onClear && <ClearIcon {...props} />}
{!onClear && (help || onHelp) && <HelpIcon {...props} />}
</div>
{props['aria-invalid'] && <span className={styles.error}>{errorMessage}</span>}
</div>
)
})
function HelpIcon(props: Props) {
if (props.onHelp) {
return (
<button type="button" className={styles.rightIcon} aria-label={props.help} onClick={props.onHelp}>
<IconFa icon={['far', 'info-circle']} title={props.help} />
</button>
)
}
return (
<div className={styles.rightIcon} title={props.help}>
<IconFa icon={['far', 'info-circle']} />
</div>
)
}
function ClearIcon(props: Props) {
return (
<button type="button" className={styles.rightIcon} aria-label={props.clear} onClick={props.onClear}>
<IconFa icon={['far', 'times']} title={props.clear} />
</button>
)
}
But, when I do it like this, the active class is added to label only when I start typing not when the input is focused. Right now, since I am using :focus selector on input, input gets active class on focus, while label gets it only after typing. How can I fix this so that they both get it on focus?
The active flag is re-evaluated only on component re-render, that is why you see a change only when the state changes (e.g. by typing, which very probably triggers a state change in the parent component if it uses onChange for example).
In your case, it seems you could just use the onFocus and onBlur listeners directly, and have the active flag as a state:
export const FormInput = forwardRef<HTMLInputElement, Props>(function FormInput(props, ref) {
const [active, setActive] = useState(false)
const labelClassName = classNames(
active && styles.active
)
return (
<div className={labelClassName}>
<input
onFocus={() => setActive(true)}
onBlur={() => setActive(false)}
/>
</div>
)
})
I was creating a project in React and faced the error of "
Unhandled Rejection (Error): Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
I tried hard to fix on my own but no result thus I really need your kind help. Here is my code:
import React, { useState, useReducer, useEffect } from "react";
import { useDropzone } from "react-dropzone";
import { Button } from "#material-ui/core";
import styles from "./dropZone.module.css";
import { useSelector, useDispatch } from "react-redux";
import { ReactComponent as ReactLogo } from "../../assets/plan.svg";
import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch";
import { ADD } from "../../store/actionTypes/actionTypes";
import { ReactCanvasAnnotation, LABEL_TYPE } from "react-canvas-annotation";
function DropZone(props) {
const fileSelector = useSelector((state) => state.fileReducer.file);
const isEditMode = useSelector((state) => state.editReducer.isEditMode);
const [annotationType, setAnnotationType] = useState(null);
const [labels, setLabels] = useState(null);
const dispatch = useDispatch();
const {
fileRejections,
isDragReject,
isDragActive,
getRootProps,
getInputProps,
} = useDropzone({
accept: "image/jpeg, image/png",
onDrop: (acceptedFiles) => {
dispatch({
type: ADD,
payload: acceptedFiles.map((file) =>
Object.assign(file, {
preview: URL.createObjectURL(file),
})
),
});
},
});
const onConsole = (msg) => (id) => console.info(msg, id);
const thumbs = fileSelector.map((file) => (
<div key={file.name}>
<img className={styles.uploadedImage} src={file.preview} />
</div>
));
useEffect(
() => () => {
// Make sure to revoke the data uris to avoid memory leaks
fileSelector.forEach((file) => URL.revokeObjectURL(file.preview));
},
[fileSelector]
);
const isFileTooLarge =
fileRejections.length > 0 && fileRejections[0].size > props.maxSize;
return (
<section className={styles.container}>
{isEditMode && (
<>
<TransformWrapper>
{({ zoomIn, zoomOut, resetTransform, ...rest }) => (
<>
<Button
className={styles.zoomIn}
variant="contained"
color="primary"
onClick={zoomIn}
>
Zoom in
</Button>
<Button
className={styles.zoomOut}
variant="contained"
color="secondary"
onClick={zoomOut}
>
Zoom out
</Button>
<Button
className={styles.reset}
variant="contained"
onClick={resetTransform}
>
x
</Button>
<TransformComponent>
<div {...getRootProps({ className: styles.dropZone })}>
{fileSelector.length > 0 && <div>{thumbs}</div>}
</div>
</TransformComponent>
</>
)}
</TransformWrapper>
</>
)}
{fileSelector.length > 0 && !isEditMode && (
<>
<ReactCanvasAnnotation
imageFile={thumbs}
labels={labels}
onChange={setLabels}
annotationType={annotationType}
onMouseOut={onConsole(`onMouseOut`)}
onHover={onConsole(`onHover`)}
onClick={onConsole(`onClick`)}
/>
<div {...getRootProps({ className: styles.dropZone })}>
{fileSelector.length > 0 && <div>{thumbs}</div>}
</div>
</>
)}
{fileSelector.length === 0 && (
<div {...getRootProps({ className: styles.dropZone })}>
{fileSelector.length === 0 && !isEditMode && (
<input {...getInputProps()} />
)}
{fileSelector.length === 0 && (
<div>
<span>{isDragActive ? <ReactLogo /> : <ReactLogo />}</span>
<h3>Click Yellow zone or drop an image to upload!</h3>
</div>
)}
{isDragReject && <h3>This file type not supported, sorry!</h3>}
{isDragActive && !isDragReject && <h3>Drop it AS IF it's hot!</h3>}
{isFileTooLarge && <div>File is too large.</div>}
{fileRejections.length > 0 && "File not supported"}
</div>
)}
</section>
);
}
export default DropZone;
I created a sandbox: https://codesandbox.io/s/happy-rgb-vks06?file=/src/App.js
I am trying to pass props to the SlotSettings component, but I get this error:
Warning: Function components cannot be given refs. Attempts to access
this ref will fail. Did you mean to use React.forwardRef()?
I tried to read both Bootstrap docs and React docs but I could not understand how this should work.
This is the code I'm using:
const SlotSettings = props => {
console.log(props.hello); // this works
return <Popover {...props} id="popover-basic">
<Popover.Title as="h3">Popover right</Popover.Title>
<Popover.Content>
And here's some <strong>amazing</strong> content. It's very engaging.
right?
</Popover.Content>
</Popover>
}
const getDaySlots = slots => {
if (slots.length >= 1) {
return slots.map(slot => {
const variant = slot.status === "free" ? "success" : "secondary";
const buttonRef = createRef();
return (
<OverlayTrigger
key={uuid()}
trigger="click"
placement="bottom"
overlay={<SlotSettings hello="hello" />}
rootClose
>
<Button size="sm" ref={buttonRef} variant={variant} style={{ margin: "8px"}}>{slot.start}</Button>
</OverlayTrigger>
)
});
}
return "No lessons available."
}
I accomplished this by utilizing the popperConfig property of OverlayTrigger.
PopperConfig is used to pass and object to the the underlying popper instance.
link to docs
Simple example:
function renderTooltip(props) {
let message = ""
//Sometimes, props.popper.state is undefined.
//It runs this function enough times that state gets a value
if (props.popper.state) {
message = props.popper.state.options.testObj
}
return (
<Tooltip id="button-tooltip" {...props}>
{message}
</Tooltip>
);
}
function getDaySlots(slots) {
//Other stuff
return (
<OverlayTrigger
placement="right"
delay={{ show: 250, hide: 400 }}
overlay={renderTooltip}
popperConfig={{testObj:"hello there"}}
>
<Button variant="success">Click here</Button>
</OverlayTrigger >
);
}
I messed with your codesandbox, but couldn't get popper to get a state value for some reason. What I posted above works for my project, hope this helps you get started.
import React, { useState ,useRef} from 'react';
import { Popover, Overlay } from 'react-bootstrap';
const DemoComponent = () => {
const [show, setShow] = useState(false);
const target = useRef(null);
return (
<div className="">
<span ref={target} onMouseEnter={() => setShow(true)} onMouseLeave={() => setShow(false)}>
open tooltip
</span>
<Overlay target={target.current} show={show} placement="top">
{(props) => (
<Popover id="popover-basic" className="customize-tooltip" {...props}>
<Popover.Body>
put here dyanamic content
</Popover.Body>
</Popover>
)}
</Overlay>
</div>
);}
export default DemoComponent;
I have a nav menu built with material-ui/core in Navbar.
I use useRef to track the position of clicked button on toggle menu close.
anchorRef.current.contains(event.target)
And I am getting 'Uncaught TypeError: anchorRef.current.contains is not a function' .
I tried 'Object.values(anchorRef.current).includes(event.target)' instead, it always returns false.
-- update --
anchorRef.current.props Object.
withStyles {
props:{
aria-haspopup: "true"
aria-owns: undefined
children: "계정"
className: "nav-menu--btn"
onClic: f onClick()
get ref: f()
isReactWarning: true
arguments: (...)
caller: (...)
length: 0
name: "warnAboutAccessingRef"
...
}, context{...}, refs{...}, ...}
ToggleMenuList
const ToggleMenuList = ({ navAdminList, navAdminItems, classes }) => {
const [activeId, setActiveId] = useState(null);
const anchorRef = useRef(null);
const handleToggle = id => {
setActiveId(id);
};
const handleClose = event => {
if (anchorRef.current && anchorRef.current.contains(event.target)) {
return;
}
setActiveId(null);
};
return (
<React.Fragment>
<div className={`nav-menu--admin ${classes.root}`}>
{navAdminList.map(e => (
<div key={e.id}>
<Button
ref={anchorRef}
aria-owns={activeId === e.id ? 'menu-list-grow' : undefined}
aria-haspopup="true"
onClick={() => handleToggle(e.id)}
>
{e.name}
</Button>
{activeId === e.id && (
<ToggleMenuItems
id={e.id}
activeId={activeId}
handleClose={handleClose}
anchorRef={anchorRef}
items={navAdminItems[e.id]}
/>
)}
</div>
))}
</div>
</React.Fragment>
);
};
export default withStyles(styles)(ToggleMenuList);
ToggleMenuItems
const ToggleMenuItems = ({
listId,
activeId,
handleClose,
anchorRef,
items,
}) => {
const isOpen = activeId === listId;
const leftSideMenu = activeId === 3 || activeId === 4 ? 'leftSideMenu' : '';
return (
<Popper
open={isOpen}
anchorEl={anchorRef.current}
keepMounted
transition
disablePortal
>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin:
placement === 'bottom' ? 'center top' : 'center bottom',
}}
className={`toggle-menu ${leftSideMenu}`}
>
<Paper id="menu-list-grow">
<ClickAwayListener
onClickAway={handleClose}
>
<MenuList className="toggle-menu--list">
{items.map(e => (
<MenuItem
key={e.id}
className="toggle-menu--item"
onClick={handleClose}
>
<Link
to={e.to}
className="anchor td-none c-text1 toggle-menu--link"
>
{e.name}
</Link>
</MenuItem>
))}
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
);
};
export default ToggleMenuItems;
react: ^16.8.6
react-dom: ^16.8.6
react-router-dom: ^4.3.1
#material-ui/core: ^3.1.2
I assume your ToggleMenuItems sets up global(document-level?) event listener on click to collapse Menu on clicking somewhere outside.
And you have a sibling button element. Clicking on that you want to keep menu expanded, right? So that was the point to use .contains in onClick to check if we are clicked outside of ToggleMenuItems but in scope of specific Button. The reason why it does not work: <Button> is custom class-based React component so it returns React component instance in ref. And it does not have any DOM-specific methods like .contains
You can rework you current approach: just stop bubbling event in case Button has been clicked. It would stop global event handler set by ToggleMenuItems to react.
const stopPropagation = (event) => event.stopPropagation();
const ToggleMenuList = ({ navAdminList, navAdminItems, classes }) => {
const [activeId, setActiveId] = useState(null);
const anchorRef = useRef(null);
const handleToggle = id => {
setActiveId(id);
};
const handleClose = event => {
setActiveId(null);
};
return (
<React.Fragment>
<div className={`nav-menu--admin ${classes.root}`}>
{navAdminList.map(e => (
<div key={e.id}>
<div onClick={stopPropagation}>
<Button
aria-owns={activeId === e.id ? 'menu-list-grow' : undefined}
aria-haspopup="true"
onClick={() => handleToggle(e.id)}
>
{e.name}
</Button>
</div>
{activeId === e.id && (
<ToggleMenuItems
id={e.id}
activeId={activeId}
handleClose={handleClose}
anchorRef={anchorRef}
items={navAdminItems[e.id]}
/>
)}
</div>
))}
</div>
</React.Fragment>
);
};
export default withStyles(styles)(ToggleMenuList);
I've put stopPropagation handler outside since it does not depend on any internal variable.