Get text Input Value React - javascript

I'm trying to track the text input value using states but "e.target.value" doesn't seem to work(maybe because my component is declared as a function). Is there any other way I can do it?
const UncontrolledDiagram = ({ sentence }) => {
// create diagrams schema
const [schema, { onChange, addNode, connect, removeNode }] = useSchema(initialSchema);
const [selected, setSelected] = useState([]);
const [textInput,setInput]=useState('')
const handleTextChange=(e)=>{
setInput(e.target.value);
console.log(textInput);
}
This is the input field I am tracking:
const conditionalDisplay=(id)=>{
const nodeToCheck=schema.nodes.find(node=>node.id=== id);
if(nodeToCheck.form===null){
return(
<div>
<label>Form: </label><input style={{ width: '25px', height: '12px' }} onChange={handleTextChange} type='text'></input>
<button className='buttonInputSubmit'>+</button>
</div>
)
}
else{
return(
<div style={{display: 'flex',margin: '0'}}>
<label>Form: </label><p style={{color: 'yellow', marginLeft:'2px'}}>{nodeToCheck.form}</p>
</div>
)
}
}

It works, your console.log(textInput) still has the old state because state is set asynchronously. It will set the state as soon as your function has been fully executed.
const handleTextChange=(e)=>{
setInput(e.target.value);
}
useEffect(() => {
console.log(textInput);
}, [textInput])

Related

Highlighting a specific word in a text document using React JS

const boldText = () => {
const text=document.getElementById('blog-text');
const originalText = text.innerHTML;
const input = document.getElementById('text-to-search');
if (input.value) {
const word=input.value.trim();
const regexp = new RegExp(word,'gi');
reactStringReplace(originalText,regexp, (match, i) => (
<span key={i} style={{ fontWeight: 'bold' }}>{match}</span>
));
}
}
I tried to use this code in order to highlight a specific word given by the user in a document(input by user) and replace the word in the original document but I am not receiving any change on clicking the button in the document.
I also tried this code below but still was awarded with no changes in the document.
*Below that I have also provided the js snippet
const boldText = () => {
let btext = document.getElementById('blog-text');
console.log(document.getElementById('blog-text').value);
let input = document.getElementById('text-to-search').value;
console.log(document.getElementById('text-to-search').value);
input = input.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");
let pattern = new RegExp(`${input}`, "gi");
reactStringReplace(btext,pattern, (match, i) => (
<span key={i} style={{ fontWeight: 'bold' }}>{match}</span>
));
}
This is the Complete JavaScript file
import React, { useState } from 'react'
import "./Blog.css"
import reactStringReplace from 'react-string-replace';
export default function Blog() {
// const boldText = () => {
// const text=document.getElementById('blog-text');
// const originalText=text.innerHTML
// const input=document.getElementById('text-to-search');
// if(input.value){
// const word=input.value.trim();
// const regexp=new RegExp(word,'gi');
// reactStringReplace(originalText,regexp, (match, i) => (
// <span key={i} style={{ fontWeight: 'bold' }}>{match}</span>
// ));
// }
const boldText = () => {
let btext = document.getElementById('blog-text');
console.log(document.getElementById('blog-text').value);
let input = document.getElementById('text-to-search').value;
console.log(document.getElementById('text-to-search').value);
input = input.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");
let pattern = new RegExp(`${input}`, "gi");
reactStringReplace(btext,pattern, (match, i) => (
<span key={i} style={{ fontWeight: 'bold' }}>{match}</span>
));
}
const handleOnChangetit = (event) => {
setTitText(event.target.value);
}
const handleOnChangedes = (event) => {
setDesText(event.target.value);
}
const [titleText, setTitText] = useState("Enter Title here....");
const [desText, setDesText] = useState("Enter Text here....");
return (
<>
<div className="blogButtons">
<input className="searchbar" type="search" id="text-to-search"
placeholder="Search" aria-label="Search..." />
<button className="btn btn-outline-success" onClick={boldText} type="submit"><i
className="fa-solid fa-magnifying-glass"> Bold Text</i></button>
</div>
<div className="textbox">
<textarea type="text" value={titleText} onChange={handleOnChangetit}
className='blogtitle' />
<textarea type="text" id="blog-text" value={desText} onChange={handleOnChangedes}
className='blogarea' />
</div>
</>
)
}
It is a bit of an anti-pattern to access the DOM directly in your react code. Because of re-rendering and rebuilding the actual DOM on screen. It is better to use hooks to build references to your JSX elements into your components for when you need to access or mutate the values.
I can't see the inputs or the blog text components, so I'll have to give you an out of context example:
import { useState, useRef, useEffect } from "React"
import reactStringReplace from 'react-string-replace'
const MyComponent = ({ blogText }) => {
const [inputValue, setInputValue] = useState('')
const blogText = useRef()
useEffect(() => {
const searchValue = inputValue.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
let pattern = new RegExp(`${searchValue}`, "gi")
reactStringReplace(blogText.current, pattern, (match, i) => (
<span key={i} style={{ fontWeight: 'bold' }}>{match}</span>
))
}, [inputValue, blogText])
return (
<div>
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
type="text"
className="text-to-search"
/>
<p className="blog-text" ref={blogText}>
{blogText}
</p>
</div>
)
}
Explanation of Hooks
useState
This is a hook that returns an array containing both a value and a function that sets the value
These values are destructured from the array at use of the hook
Assign the value to your input, and an onChange handler that updates the values when the user types
useEffect
Calls a function whenever a render is triggered
The render can be targeted to a specific change, in this case, the change of the input
Put the targeted change in the Dependency Array, the second argument to useEffect
useRef
Use this function to assign a value to a DOM element that will persist between renders.
This value is "safe" from rerenders, and is safe to use inside a dependency array of useEffect for example.
Use this instead of DOM methods
Access the referenced value with blogText.current
Essentially, I am using useEffect to listen for changes to inputValue, which is using State to control the text input. When this state value changes, it runs a function that calls reactStringReplace with the reference to blogText.

How to pass argument to ref click method using react and javascript?

i want to pass an argument to ref click method using javascript.
what i am trying to do?
there is a list of cards with each card having more button . clicking more button would open up a select menu with options edit, upload file and remove.
now clicking upload file option should allow user to upload file for that card.
below is the code,
const Parent = (data) => {
const fileInput = React.useRef(null);
const handleUploadClick = (id) => {
console.log('id in here', id); //this is the correct id. meaning this is the id of the
upload file button clicked for the card.
fileInput.current?.click();
}, [fileInput.current]);
return(
<>
{cardData.map(data, index) => {
const {description, id } = data;
console.log('id in map', id )
const uploadFile = (
<button onClick={() => handleUploadClick(id)}> Upload file </span>
)
const edit = (//somelogic)
const remove = (//some logic)
const options = compact([edit, uploadFile, remove]);
return (
<Card
id={id}
options={options}
>
<input type="file" ref={fileInput} style={display: 'none'}
onChange={async () => {
const file = fileInput?.current?.files?.[0];
try(
const input = {
file: file,
}
await updateFile({
variables: {
id: id!, //here id is the id of the last card so it always uploads file for last card. but not the actual card for which the upload file button
//clicked.
input,
},
});
</Card>
</>
);
}
Now the problem with above code, is in handleUploadclick method the id is correct. however handleUploadClick method triggers input type="file" element click. but in the onchange method of this input type="file" element the id is not correct. it is always the id of the last card. and hence it uploads file to the last card only. meaning it passes wrong id to the updateFile method in onchange method of input type="file".
i am not sure how to pass id to fileInput.current?.click() method in handleUploadClick or is there any other solution to fix this problem.
could someone please help me with this. thanks.
in your case you shouldn't using useRef , all you need to do is to use useState and useEffect to handle the change with passing the keys properly, you can save the file after user upload file using onChange function
const [file, setFile] = useState(null);
const handleUploadClick = () => {
console.log(file)
}
<button key={`button-${index}`} onClick={() => handleUploadClick()}> Upload file </button>
<input type="file" key={`input-${index}`} ref={fileInput} style={display: 'none'}
onChange={(e) => setFile(e.target.files[0])}
/>
folk here is the answer to your question, so let me explain first what I have done, as of your implementation the ref is not sustained as it's being replaced by every next item you return in array.map() so here we go we managed all array items refs in an itemsRef array so when we click on the specific button we can get the element/input by it's id.
import React from "react";
const inputs = [
{
name: "Input one",
id: 1
},
{
name: "Input two",
id: 2
}
];
const App = () => {
// to hold all inputs refs
const itemsRef = React.useRef([]);
// to create an empty array of inputs lenght so we can hold refs later
React.useEffect(() => {
itemsRef.current = itemsRef.current.slice(0, inputs.length);
}, []);
// to triger clicked button relative input
const handleUploadClick = React.useCallback(
(id) => {
console.log("id in here", id); //this is the correct id. meaning this is the id of the
const eleByRefId = itemsRef?.current[id]; // ref by id
eleByRefId && eleByRefId.click();
},
[itemsRef]
);
// your file uploading logics here
const handleFileChange = React.useCallback(async (e, id) => {
const file = e.target.files[0];
// your file uploading logic
// await updateFile({
// variables: {
// id: id,
// input,
// },
// });
}, []);
return (
<div style={{ display: "flex", flexDirection: "row" }}>
{inputs.map((data, index) => {
const { id, name } = data;
return (
<div key={index} style={{ marginRight: 10 }}>
<input
type="file"
key={id}
ref={(el) => (itemsRef.current[id] = el)} // the magic part is happening here
style={{ display: "none" }}
onChange={(e) => handleFileChange(e, id)}
/>
<button onClick={() => handleUploadClick(id)}>{name}</button>
</div>
);
})}
</div>
);
};
export default React.memo(App);
Here is the codesandbox

How to set Formik custom component value to Formik value

I'm using Formik for my form with google place auto-complete, I want to render places auto-complete as a custom component in the Formik field.
form.js
<Formik initialValues={location:""}>
<Field name="location" component={PlacesAutoComplete} placeholder="enter your location"/>
{...rest of form}
</Formik>
auto-complete component
import PlacesAutocomplete , {
geocodeByAddress,
geocodeByPlaceId
} from "react-google-places-autocomplete";
export const PlacesAutoComplete = ({
field: { name, ...field }, // { name, value, onChange, onBlur }
form: { touched, errors }, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
classes,
label,
...props
}: any) => {
const [fieldName, setFildName] = React.useState(field.name);
const [address, setAddress] = React.useState(props.value || "");
const error = errors[name];
// const touch = touched[name];
const handleSelect = () => {
// set this value to formik value
};
const handleChange = () => {
// set this value to formik value
};
const handleError = () => {
props.form.setFieldError(fieldName, error);
};
return (
<PlacesAutocomplete
value={address}
onChange={handleChange}
onSelect={handleSelect}
onError={handleError}
name={name}
placeholder={props.placeholder}
id={name}
{...props}
apiKey="Api key here"
>
{({
getInputProps,
suggestions,
getSuggestionItemProps,
loading
}: any) => (
<div>
<input
{...getInputProps({
placeholder: "Search Places ...",
className: "location-search-input form-control"
})}
/>
<div className="autocomplete-dropdown-container">
{loading && <div>Loading...</div>}
{suggestions.map((suggestion: any) => {
const className = suggestion.active
? "suggestion-item--active"
: "suggestion-item";
// inline style for demonstration purpose
const style = suggestion.active
? { backgroundColor: "#fafafa", cursor: "pointer" }
: { backgroundColor: "#ffffff", cursor: "pointer" };
return (
<div
{...getSuggestionItemProps(suggestion, {
className,
style
})}
>
<span>{suggestion.description}</span>
</div>
);
})}
</div>
</div>
)}
</PlacesAutocomplete>
);
};
How I set places auto-complete value to formik value, I'm pretty new to react and confused in handle change and on change functions. also, I found a solution in react class component here, But when converting those codes into functional components I'm stuck in Onchange and onSlecet functions
Better not write functional components as you'll get stuck with the test cases if you are writing.
OnChange is even you type anything, the value gets stored in onChange.
Abe onSelect is when you select anything
Basically on change you need to call formik's field onChange function. So in case you get an event on handleChange, just do this
const handleChange = (event) => {
// set this value to formik value
field.onChange(event.target.value)
};
or in case you get value in handleChange then do this
const handleChange = (value) => {
// set this value to formik value
field.onChange(value)
};
This will sync your formik state with autocomplete state.
Now comes the part for select. In this case also you can take the same route
const handleSelect = (value) => {
// set this value to formik value
field.onChange(value)
};
or you can use the setField function of form to update the value
const handleSelect = (value) => {
// set this value to formik value
form.setField('location',value)
};

Conditionally render part of object onClick inside a map (REACT.js)

I am trying to conditionally render part of an object (user comment) onClick of button.
The objects are being pulled from a Firebase Database.
I have multiple objects and want to only render comments for the Result component I click on.
The user comment is stored in the same object as all the other information such as name, date and ratings.
My original approach was to set a boolean value of false to each Result component and try to change this value to false but cannot seem to get it working.
Code and images attached below, any help would be greatly appreciated.
{
accumRating: 3.7
adheranceRating: 4
cleanRating: 2
date: "2020-10-10"
place: "PYGMALIAN"
staffRating: 5
timestamp: t {seconds: 1603315308, nanoseconds: 772000000}
userComment: "Bad"
viewComment: false
}
const results = props.data.map((item, index) => {
return (
<div className='Results' key={index}>
<span>{item.place}</span>
<span>{item.date}</span>
<Rating
name={'read-only'}
value={item.accumRating}
style={{
width: 'auto',
alignItems: 'center',
}}
/>
<button>i</button>
{/* <span>{item.userComment}</span> */}
</div >
)
})
You have to track individual state of each button toggle in that case.
The solution I think of is not the best but you could create a click handler for the button and adding a classname for the span then check if that class exists. If it exists then, just hide the comment.
Just make sure that the next sibling of the button is the target you want to hide/show
const toggleComment = (e) => {
const sibling = e.target.nextElementSibling;
sibling.classList.toggle('is-visible');
if (sibling.classList.contains('is-visible')) {
sibling.style.display = 'none'; // or set visibility to hidden
} else {
sibling.style.display = 'inline-block'; // or set visibility to visible
}
}
<button onClick={toggleComment}>i</button>
<span>{item.userComment}</span>
You can try like this:
const [backendData, setBackendData] = useState([]);
...
const showCommentsHandler = (viewComment, index) => {
let clonedBackendData = [...this.state.backendData];
clonedBackendData[index].viewComment = !viewComment;
setBackendData(clonedBackendData);
}
....
return(
<div>
....
<button onClick={() => showCommentsHandler(item.viewComment, index)}>i</button>
{item.viewComment && item.userComment}
<div>
You can store an array with that places which are clicked, for example:
const [ selectedItems, setSelectedItems] = React.useState([]);
const onClick = (el) => {
if (selectedItems.includes(el.place)) {
setSelectedItems(selectedItems.filter(e => e.place !== el.place));
} else {
setSelectedItems(selectedItems.concat(el));
}
}
and in your render function
const results = props.data.map((item, index) => {
return (
<div className='Results' key={index}>
<span>{item.place}</span>
<span>{item.date}</span>
<Rating
name={'read-only'}
value={item.accumRating}
style={{
width: 'auto',
alignItems: 'center',
}}
/>
<button onClick={() => onClick(item)}>i</button>
{ /* HERE */ }
{ selectedItems.includes(item.place) && <span>{item.userComment}</span> }
</div >
)
})
You need to use useState or your component won't update even if you change the property from false to true.
In order to do so you need an id since you might have more than one post.
(Actually you have a timestamp already, you can use that instead of an id.)
const [posts, setPosts] = useState([
{
id: 1,
accumRating: 3.7,
adheranceRating: 4,
cleanRating: 2,
date: "2020-10-10",
place: "PYGMALIAN",
staffRating: 5,
timestamp: { seconds: 1603315308, nanoseconds: 772000000 },
userComment: "Bad",
viewComment: false
}
]);
Create a function that updates the single property and then updates the state.
const handleClick = (id) => {
const singlePost = posts.findIndex((post) => post.id === id);
const newPosts = [...posts];
newPosts[singlePost] = {
...newPosts[singlePost],
viewComment: !newPosts[singlePost].viewComment
};
setPosts(newPosts);
};
Then you can conditionally render the comment.
return (
<div className="Results" key={index}>
<span>{item.place}</span>
<span>{item.date}</span>
<Rating
name={"read-only"}
value={item.accumRating}
style={{
width: "auto",
alignItems: "center"
}}
/>
<button onClick={() => handleClick(item.id)}>i</button>
{item.viewComment && <span>{item.userComment}</span>}
</div>
);
Check this codesandbox to see how it works.

Invalid hook call when I'm setting values from numpad using useChange

This is my component:
const pricePicker = ({
step,
precision,
input,
placeholder,
label,
theme,
props,
meta: { touched, error },
...rest
}) => {
/*In the FOLLOWING LINES from "function useChange(e)" to "return [value,change]"
is the error known as Invalid hook.
*/
function useChange(e){
const [value,setValue] = useState(0);
function change(event){
setValue(value => event.target.value);
}
return [value,change];
}
const handleBlur = (e) => {
if (e.target.value === '0') e.target.value = '0'
}
const handleKeypress = (e) => {
const characterCode = e.key
if (characterCode === 'Backspace') return
const characterNumber = Number(characterCode)
if (characterNumber < 0) {
e.preventDefault()
}
}
const myTheme = {
fontFamily: 'Arial',
textAlign: 'center',
header: {
primaryColor: '#263238',
secondaryColor: '#f9f9f9',
highlightColor: '#FFC107',
backgroundColor: '#607D8B',
},
body: {
primaryColor: '#263238',
secondaryColor: '#32a5f2',
highlightColor: '#FFC107',
backgroundColor: '#f9f9f9',
},
panel: {
backgroundColor: '#CFD8DC'
}
};
return(
<div className='form-group'>
<label forname={input.name}>{label}</label> <br />
<NumPad.Number
{...rest}
step={0.1}
precision={2}
placeholder={!input.value ? 'Please, type a number' : input.value}
selected={input.value ? new NumPad.Number(input.value) : null}
onKeyDown={(changedVal) => handleKeypress(changedVal)}
onBlur={(changedVal) => handleBlur(changedVal)}
onChange={(changedVal) => useChange(changedVal)}
className='form-control'
/>
<div className='text-danger' style={{ marginBottom: '20px' }}>
{touched && error}
</div>
</div>
);
};
export default pricePicker;
When I'm executing this block of code:
function useChange(e){
const [value,setValue] = useState(0);
function change(event){
setValue(value => event.target.value);
}
return [value,change];
}
I'm getting the following issue:
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've tried all ways but it seems it's impossible. I never used hooks and previously I post about something similar but unsuccesfully. Previous post talks about useState is inside pricePicker function is neither a funcional component or react hook component when executed previous code lines like this:
const handleChange = (e) =>{
// in the following line is the error.
const[value, setValue] = useState(0);
}
How can I solve this issue? I need to fix it, but how? I've tried all ways but unsuccessfully.
Any one knows how can I fix this issue? It's important.
The error is actually quite simple - hooks can be used only at the top level of functional components. In your concrete example, you cannot use useState inside of function useChange.
Instead, do something like:
const pricePicker = ({/*props go here*/
const [value,setValue] = useState(0);
// handler of input change
const onChange = e => setValue(e.target.value);
// the rest of the code can stay the same
return <div className='form-group'>
<label forname={input.name}>{label}</label> <br />
<NumPad.Number
{...rest}
step={0.1}
precision={2}
placeholder={!input.value ? 'Please, type a number' : input.value}
selected={input.value ? new NumPad.Number(input.value) : null}
onKeyDown={(changedVal) => handleKeypress(changedVal)}
onBlur={(changedVal) => handleBlur(changedVal)}
onChange={onChange} /* Here's we use the onChange handler */
className='form-control'
/>
<div className='text-danger' style={{ marginBottom: '20px' }}>
{touched && error}
</div>
</div>;
}

Categories