how to fit dropdown till last position of text-field in react? - javascript

in my code , dropdown shown when user type $ symbol ,this is working good till you gave 22 character's I wanted to when user gave more character that dropdown should be set at last position but now dropdown is going to out of the frame of text field ,so how to fix that the dropdown should move relative to the cursor on the current text only?
this should be the last position if user give more than 22 characters but now I am facing problem if i gave more than 22 characters and type $ symbol
import { useState, useRef } from "react";
export default function App() {
const [value, setValue] = useState("");
const [show, setShow] = useState(false);
const [cursor, setCursor] = useState(0);
const ref = useRef(null);
const foo = (e) => {
setValue(e.target.value);
if (e.nativeEvent.data === "$") {
setShow(true);
} else {
setShow(false);
}
setCursor(e.target.value?.length*8)
};
return (
<div className="App">
<input value={value} onChange={foo} ref={ref} />
{show && (
<div
style={{
width: "200px",
height: "200px",
background: "gray",
marginLeft: cursor + "px",
position: "absolute",
left: "0px"
}}
>
DropDown
</div>
)}
</div>
);
}

Related

onchange event should trigger at the time of onclick event in react

in that component I need to print onenumber as a text in the textbox in a way like I am selecting one from dropdown and typing number , so at a time we are able to type text in a text box and selecting text from the dropdown ,and in on change function triggered all time like if you type text and select the dropdown as well ,so what should I changed in the on Change event?
import { useState } from "react";
export default function App() {
const [show, setShow] = useState(true);
const [val, setVal] = useState("");
function handleClick(event) {
setVal(event.target.innerHTML);
setShow(false);
}
function handleChange(event) {
setVal(event.target.value);
console.log(event.target.value);
}
return (
<div className="App">
<input
type="text"
value={val}
onChange={handleChange}
onKeyUp={handleClick}
/>
{show && (
<div
style={{
width: "180px",
height: "80px",
background: "pink"
}}
onClick={handleClick}
>
<div>one</div>
<div>two</div>
<div>three</div>
<div>four</div>
</div>
)}
</div>
);
}
Try this:
import React from 'react'
import { useState } from "react";
function Test() {
const [show, setShow] = useState(true);
const [val, setVal] = useState("");
function handleClick(event, what) {
if (what === 'click') { setVal(event.target.innerHTML) }
setShow(false);
}
function handleChange(event) {
setVal(event.target.value);
}
return (
<div className="App">
<input
type="text"
value={val}
onChange={handleChange}
onKeyUp={(e) => handleClick(e, 'keyup')}
/>
{show && (
<div
style={{
width: "180px",
height: "80px",
background: "pink"
}}
onClick={(e) => handleClick(e, 'click')}
>
<div>one</div>
<div>two</div>
<div>three</div>
<div>four</div>
</div>
)}
</div>
);
}
export default Test
you can check the condition, That show state is true of false in handleChange and then print number as a text in the textbox

How to avoid re rendering text input?

I have a TextInput and I don't want it to re render every time I change the value inside it
const [WrittenVal,setWrittenVal] = useState(()=>'');
...
<TextInput
value={String(WrittenVal)}
onChangeText={text => setWrittenVal(text)}
/>
but I want to be able to change the value inside the input at the push of a button that's why I haven't just used defaultValue
any solutions??
You can use useRef to save text from text input without render , and useState to show text in input on button press:
Live example : https://snack.expo.dev/TW-fMx1-2
import React from "react";
import { SafeAreaView, StyleSheet, TextInput,TouchableOpacity,Text } from "react-native";
const UselessTextInput = () => {
const [text, onChangeText] = React.useState("");
const textRef = React.useRef('')
const textToRef = (text) =>{
textRef.current = textRef.current + text
}
const showTextInInput = () =>{
onChangeText( textRef.current )
}
console.log("render")
return (
<SafeAreaView>
<TextInput
style={styles.input}
onChangeText={textToRef}
value={text}
/>
<TouchableOpacity onPress={showTextInInput}>
<Text>SHOW TEXT IN INPUT</Text>
</TouchableOpacity>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
input: {
height: 40,
margin: 12,
borderWidth: 1,
marginTop:50,
padding: 10,
},
});
export default UselessTextInput;
const inputRef = useRef();
<TextInput
ref={inputRef}
onChangeText={text => inputRef.text = text }
/>
//function to get input value
const handleSubmit = () => {
let textInputValue = inputRef.text;
}
You cannot prevent re-renders on type. But your code can be simplified to:
const [value, setValue] = useState('');
<TextInput
value={value}
onChangeText={setValue}
/>
You can't prevent re-render on input when the value change.
But you can prevent other components to be re-renderd by React.memo or useMemo hook.
And for changing value of input with button press you can do like this:
<Button onPress={() => {
setWrittenVal(""); //write value you want in ""
}}
If you have a nested component situation like the below you need to move the state into the component (under EpicComponent) the setWrittenVal will trigger a re-render on the EpicComponent. Common symptoms are the the field will lose focus when you type.
const Parent = ()=> {
const [WrittenVal,setWrittenVal] = useState(()=>'');
...
const EpicComponent = ()=> {
return (
<TextInput
value={String(WrittenVal)}
onChangeText={text => setWrittenVal(text)}
/> )
}
return (<EpicComponent/>)
}

Filtering data and returning it next to the "permanent" data - react

I added a TextField from the MUI library, and used a useRef hook to capture the value "live" as the user types something. The intention is to filter only the rates which include the characters he types. As of right now:
Object.keys(rates["rates"]) // ["EUR", "RON", "CZK", ...]
I added a form, and I want it to stay persistent, but the buttons should change dynamically. If the user has not typed anything I want to return everything (like nothing is filtered)
My try:
import React, {useEffect, useRef, useState} from 'react'
import Button from '#mui/material/Button';
import CircularProgress from '#mui/material/CircularProgress';
import Box from '#mui/material/Box';
import TextField from '#mui/material/TextField';
const RatesButton = () => {
const rateRef = useRef('')
const [rates, setRates] = useState([]);
useEffect(
() => {
fetch("https://api.vatcomply.com/rates")
.then(ratesResponse => ratesResponse.json())
.then(rates => setRates(rates));
}
, [])
if (rates.length === 0) {
return (
<Box sx={{display: 'flex', justifyContent: 'center'}}>
<CircularProgress/>
</Box>
)
}
const rateSearch = () => {
Object.keys(rates["rates"]).filter(
rate => rate.includes(rateRef.current.value)
).map(rate => {
return (
<Button>
{rate}
</Button>
)
}
)
}
return (
<>
<br/>
<TextField id="rate-search" onChange={rateSearch} inputRef={rateRef} label="Rate" variant="outlined"/>
</>
)
}
export default RatesButton
It works nicely I think, I can access the reference of the input of the user, filter all the rates that contain the letters, and map each one to a MUI Button. The problem is that they don't show somehow, and I am pretty lost, it is pretty confusing how I can return from two different functions at the same time, while keeping one persistent (the input field)
The buttons do not show unfortunately...
You should use controlled mode and store your TextField's value in a state using useState instead of useRef because changing the ref value doesn't trigger a re-render so the UI doesn't get updated. There are a lot of other wrong things in your code, I've fixed it all, feel free to ask me if you don't understand anything:
const RatesButton = () => {
const [value, setValue] = useState("");
const [rates, setRates] = useState({});
useEffect(() => {
fetch("https://api.vatcomply.com/rates")
.then((ratesResponse) => ratesResponse.json())
.then((rates) => setRates(rates.rates ?? {}));
}, []);
return (
<>
{Object.keys(rates).length === 0 && (
<Box sx={{ display: "flex", justifyContent: "center" }}>
<CircularProgress />
</Box>
)}
{Object.keys(rates)
.filter((rate) => rate.toLowerCase().includes(value.toLowerCase()))
.map((rate) => {
return <Button>{rate}</Button>;
})}
<br />
<TextField
id="rate-search"
onChange={(e) => setValue(e.target.value)}
// inputRef={value}
label="Rate"
variant="outlined"
/>
</>
);
};
Live Demo
import React, {useState} from 'react';
import { throttle } from 'lodash';
const RatesButton = () => {
const [value, setValue] = useState("");
const [rates, setRates] = useState({});
useEffect(() => {
fetch("https://api.vatcomply.com/rates")
.then((ratesResponse) => ratesResponse.json())
.then((rates) => setRates(rates.rates ?? {}));
}, []);
const handleChange = (e) => setValue(e.target.value);
// it will prevent multiple render during fast typing
const throttledChange = throttle(handleChange, 400);
return (
<>
{Object.keys(rates).length === 0 && (
<Box sx={{ display: "flex", justifyContent: "center" }}>
<CircularProgress />
</Box>
)}
{Object.keys(rates)
.filter((rate) => rate.toLowerCase().includes(value.toLowerCase()))
.map((rate) => {
return <Button>{rate}</Button>;
})}
<br />
<TextField
id="rate-search"
onChange={throttledChange} // don't use arrow function
label="Rate"
variant="outlined"
/>
</>
);
};

Form resets stored components upon submission

I want to make an editable piece of display text, such that:
The text itself is not a textarea/input, it is just standard text
Clicking on the text will open a text area and edit button, which allows you to edit the text
Clicking the edit button will update the display text, and hide the textarea/edit button (display:none)
I have implemented every part of this, and now have a bug where any time I hit the 'edit' button, all of the text components are erased from the page entirely.
To achieve this, I have a Page component which stores a series of EditableText components (you can add more EditableText components via a + button at the bottom of the page):
const Page = (props) => {
const classes = useStyles()
var [components, setComponents] = useState([])
var [displayButton, setDisplayButton] = useState(false)
const toggleButton = () => {
displayButton = !displayButton
setDisplayButton(displayButton)
}
const addCaution = () => {
setComponents(components.concat(<Caution />))
displayButton = !displayButton
setDisplayButton(displayButton)
}
const addImportant = () => {
setComponents(components.concat(<Important />))
displayButton = !displayButton
setDisplayButton(displayButton)
}
const opbutton = (
<div>
<Button onClick={addHeader}>header</Button>
<Button onClick={addText}>text</Button>
<Button onClick={addCaution}>caution</Button>
<Button onClick={addImportant}>important</Button>
</div>
)
return (
<div className={classes.page}>
{components}
{displayButton ? opbutton : null}
<Button onClick={toggleButton}>+</Button>
</div>
)
The EditableText components are added into an array via useState.
Here is the code for the EditableText:
const EditableText = (props) => {
const classes = useStyles()
const { inputs, handleInputChange, handleSubmit } = useSubmit()
var [displayText, setDisplayText] = useState("click here to add notes!")
var [showBox, setShowBox] = useState(false)
var [showClickArea, setClickArea] = useState(true)
const inputBox = (
<form
onSubmit={handleSubmit}
style={{ display: "flex", flexDirection: "row", width: "100%" }}
>
<textarea
type="text"
name="displayText"
className={classes.textbox}
onChange={handleInputChange}
value={inputs}
style={{ borderColor: props.border }}
onSubmit={"return inputs"}
>
{inputs}
</textarea>
<Button id="editbutton" type="submit" className={classes.button}>
edit
</Button>
</form>
)
const toggleEdit = () => {
showClickArea = !showClickArea
setClickArea(showClickArea)
showBox = !showBox
setShowBox(showBox)
}
const clickArea = (
<div
style={{ width: "80%", height: "200%", position: "absolute" }}
onClick={toggleEdit}
/>
)
return (
<div>
{showClickArea ? clickArea : null}
<div className={classes.background} style={{ color: props.color }}>
{inputs}
<div>{showBox ? inputBox : displayText}</div>
</div>
</div>
)
}
You may notice the use of useSubmit which is a custom hook:
const useSubmit = (callback) => {
const [input, setInput] = useState({})
const handleSubmit = (event) => {
callback()
}
const handleInputChange = (event) => {
event.persist()
setInput((input) => ({
...input,
[event.target.name]: [event.target.value],
}))
}
return (handleSubmit, handleInputChange, input)
}
What I figure is that this may be an issue with the custom hook, or the use of form and submit. I think form is supposed to clear the page once submitted. But I'm not entirely sure. Any idea how to prevent this?
Solved by creating a function and an 'edit' variable:
function setDisplayState() {
if (edit === false) {
return (
<div
style={{
color: props.color,
fontWeight: props.weight ? props.weight : 400,
fontSize: props.size ? props.size : 14,
}}
>
{displayText}
</div>
)
} else {
return (
<div>
<textarea
className={classes.textbox}
style={{ color: props.color }}
value={displayText}
onChange={(e) => {
displayText = e.target.value
setDisplayText(displayText)
}}
/>
<Button className={classes.button} onClick={saveChange}>
save
</Button>
</div>
)
}
}

React Modal selected data dissapears after it closes

I have the following problem my selected option dissapears after i close and reopen modal , someone told me this is because all data gets lost when a modal unmounts? So how do i cause the selected data to still show.
I am able to retrieve the selected data but all i want is the selected option to still be there once modal closes and reopens
Also My select component is not reusable any clue how i could optimize that piece of code
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Modal from '#material-ui/core/Modal';
import Backdrop from '#material-ui/core/Backdrop';
import Fade from '#material-ui/core/Fade';
import Button from '../Button/Button'
const useStyles = makeStyles(theme => ({
modal: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
paper: {
backgroundColor: theme.palette.background.paper,
border: '2px solid #000',
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
}));
const TransitionsModal =(props) => {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
<Button clicked={handleOpen}></Button>
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
className={classes.modal}
open={open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={open}>
{props.children}
</Fade>
</Modal>
</div>
);
}
export default TransitionsModal
Here is my select component
import React from 'react';
import InputLabel from '#material-ui/core/InputLabel';
import FormControl from '#material-ui/core/FormControl';
import Select from '#material-ui/core/Select';
export default function NativeSelects(props) {
const [value, setValue] = React.useState('');
const handleChange = event => {
setValue(event.target.value);
console.log(event.target.value)
};
const data = props.list.map((item, id) => {
if (item.reciter_name_eng) {
return <option key={item.reciter_name_eng + id} value={item.id}>{item.reciter_name_eng} {item.style}</option>
}
//if its not null
if (item.name_simple) {
return <option key={item.name_simple + id} value={item.chapter_number}>{item.chapter_number}. {item.name_simple}</option>
}
if (item.language_name) {
return <option key={item.language_name + id} value={item.id}>{item.language_name} by {item.author_name}</option>
}
return null
})
return (
<div>
<FormControl variant="filled">
<InputLabel htmlFor="filled-age-native-simple">{props.type}</InputLabel>
<Select
native
value={value}
onChange={(event) => { props.changed(event.target.value, props.type); handleChange(event) }}>
<option value={null}> </option>
}
{data}
</Select>
</FormControl>
</div>
);
}
The problem is the default state of your Function Component is always an empty string i.e
const [value, setValue] = useState('');
When the modal is closed, the components are all unmounted, meaning when the modal is then reshown these are remounted and as a result the value will go back to an empty string.
You need to keep track of the selected value TransitionModel component and then pass this back into NativeSelects as a prop in order to set the default value i.e.
<NativeSelect defaultValue={...} />
Then in your component
function NativeSelect(props) {
const [value, setValue] = useState(props.defaultValue);
...
}

Categories

Resources