Persistent way for getting an Input Value using Hooks - reactjs

I'm a beginner at React. I'm trying to build a component that recovers the text of an input and later on inserts it on the HTML.
import React, { useState } from 'react';
function InputGetter(){
const [text, setText] = useState("")
let typed = ""
return(
<div>
<input type='text' onChange = {(e)=> typed = e.target.value}></input>
<button onClick={() => setText(typed)}>Set Text</button>
Text typed was "{text}"
</div>
)
}
export default InputGetter
The code above works for clicks when there's a change triggered. However, when I try to double click it, even when there's content on the input, text somehow is set to "". I'm not sure why it happens. How can I make the textvariable content persistent?

You have closure on text value within the onClick callback, to persist value between renders, you can use useRef hook, see Is there something like instance variables?.
function InputGetter() {
const [text, setText] = useState('');
const inputRef = useRef('');
return (
<div>
<input type="text" ref={inputRef}></input>
<button onClick={() => setText(inputRef.current.value)}>Set Text</button>
Text typed was "{text}"
</div>
);
}

Related

Adding updated state to code editor only works once

I have an issue with my code below. When you click the add code button, it adds the code to the monaco code editor which is great. However, if you type some more code in the editor or erase whats currently there and then press the 'Add code' button, nothing is added. It's just blank.
Is there a way to whenever that 'Add code' button is clicked it clears everything in the editor and just adds the setAddCode state when the button is clicked?
And here is the code:
import { useState, useRef, useEffect } from "react";
import Editor from "#monaco-editor/react";
export default function IndexPage() {
const [input, setInput] = useState("");
const [addCode, setAddCode] = useState("# code goes here");
const editorRef = useRef(null);
function handleEditorDidMount(editor, monaco) {
setInput((editorRef.current = editor));
editorRef.current = editor;
}
return (
<div>
<div className="code-editor">
<Editor
width="100vh"
height="40vh"
theme="vs-dark"
fontSize="14"
defaultLanguage="python"
defaultValue=""
value={addCode}
onMount={handleEditorDidMount}
/>
</div>
<br />
<button onClick={() => setAddCode("print('Hello World!')")}>
Add code
</button>
</div>
);
}
The way the Editor component is set up, it will only change the value in the editor if you pass a different value prop. It's probably doing something similar to the following:
const Editor = ({ value }) => {
const [codeToDisplay, setCodeToDisplay] = useState(value);
useEffect(() => {
setCodeToDisplay(value);
}, [value]);
// etc
In your parent component, when you call setAddCode("print('Hello World!')") the first time, that'll result in the child component seeing a difference in how it was called - the value prop changed - so it'll know that there's something different to display, and update its own internal state appropriately. When you press the button again, the addCode value will stay the same - the Editor component doesn't see any difference, so it won't update.
To fix it, you can listen for changes from inside Editor by using its onchange prop to update the state in the parent component when the code inside the editor changes - that way, when you click the button later, the prop will be different, and the editor will know to update its internal state.
export default function IndexPage() {
const [input, setInput] = useState("");
const [codeValue, setCodeValue] = useState("# code goes here");
const editorRef = useRef(null);
function handleEditorDidMount(editor, monaco) {
setInput((editorRef.current = editor));
editorRef.current = editor;
}
return (
<div>
<div className="code-editor">
<Editor
width="100vh"
height="40vh"
theme="vs-dark"
fontSize="14"
defaultLanguage="python"
defaultValue=""
value={codeValue}
onChange={(newValue) => { setCodeValue(newValue); }}
onMount={handleEditorDidMount}
/>
</div>
<br />
<button onClick={() => setCodeValue("print('Hello World!')")}>
Add code
</button>
</div>
);
}

How to add TextArea while clicking on image using React JS?

###This is how the code looks like ###
const message = () => {
console.log("Hello World!")
}
return(
<label htmlFor="myInput" ><ShoppingBagIcon style={{width:'26px'}} type="shoppingbag" /></label>
<input id="myInput" type="text" style={{display:'none'}} onClick={message} />)}
export default message
You could use a hook const [shown, setShown] = useState(false)
and render the TextArea based on the value of the hook.
An oversimplified example:
const [shown, setShown] = useState(false)
return (
<>
<button on:click={() => setShown(!shown)}/>
{shown ? <TextArea/> : <WithoutTextArea/>}
</>
);
You can use an extra state say, toggleView in your project. And when a user clicks the image you can set the value true or false. Based on that you can show the textField or not. What I meant is
import React,{useState} from 'react'
const message = () => {
const [showText,toggleShowText] = useState(false);
console.log("Hello World!")
const toggleTextField = () => toggleShowText(!showText)
return(
<div>
<label htmlFor="myInput" ><ShoppingBagIcon style={{width:'26px'}} type="shoppingbag" onClick ={()=>toggleShowText(!showText)}/></label>
{
showText ? (
<input id="myInput" type="text" onClick={message} />
) : (<div/>)
}
</div>
);
}
export default message;
Also in your code, the curly braces are not formatted, I did that Above example.
Also, you can use the ternary operator inside the HTML tag.
<input id="myInput" type="text" style={showText ? {display:'none'}: {display:'inline'}} onClick={message} />
or
style={{display: showText ? 'block' : 'none'}}
I Think the best way of doing hiding or showing an HTML element is using ref
import useRef from react
import { useRef } from "react";
Create a ref
const inputRef = useRef();
Use it in the input or textarea
<input
ref={inputRef}
id="myInput"
type="text"
style={{ display: "none" }}
/>
Now you can change it's any style like this
inputRef.current.style.display = "block";
Why ref?
Because it do not re-render the component. If you use state it will cost a render but using ref it won't
here is the solve
Updated
If you want to hide any elements you can do that like this bellow
ref_variable.current.style.display = 'none'
So when and which element you want to hide just make the display none using the ref.
For example if you want to hide the textarea after input. Just do that.

How to make react input show state value?

I have title and message variables as the local state in my React component. The idea is that when the component mounts, these two variables get initialised either with the respective values coming from currentPost object which is coming from Redux, or otherwise with an empty string.
const MyComponent = ({ currentPost }) => {
const [title, setTitle] = React.useState(currentPost.title ? currentPost.title : '');
const [message, setMessage] = React.useState(currentPost.message ? currentPost.message : '');
<form>
<div className="form-group">
<input
value={title}
onChange={evt => setTitle(evt.target.value)}
/>
</div>
<div className="form-group">
<input
value={title}
onChange={evt => setTitle(evt.target.value)}
/>
</div>
</form>
const mapStateToProps = state => ({ currentPost: state.posts.currentPost });
};
The code above is actually a modal that opens whenever I click on the edit icon of a given post. In the same time, the currentPost object on Redux state, which is empty, gets populated with the selected post. Then in the above modal component, I get currentPost from Redux state.
The problem is that the two variables always get an empty string, even when currentPost is populated with all the data I need. How can I make the input fields show the right data depending on the currentPost state?
Your logic is correct, there is nothing wrong with it. Just notice that the useState will only be defined when the component renders for the FIRST time. Your redux state is probably being updated only after the component has mounted already.
A quick way of checking that is just to console.log before defining the state.
const MyComponent = ({ currentPost }) => {
// This will probably show "undefined" in the first log
console.log('current Post', currentPost);
const [title, setTitle] = React.useState(currentPost.title ? currentPost.title : '');
const [message, setMessage] = React.useState(currentPost.message ? currentPost.message : '');
<form>
<div className="form-group">
<input
value={title}
onChange={evt => setTitle(evt.target.value)}
/>
</div>
<div className="form-group">
<input
value={title}
onChange={evt => setTitle(evt.target.value)}
/>
</div>
</form>
const mapStateToProps = state => ({ currentPost: state.posts.currentPost });
};
There are two ways of going about it. You can either use the redux state straight into the input and onChange will call a method to update that same state
OR
Use useEffect to update the local state once currentPost changes.
useEffect(() => {
setTitle(currentPost.title);
setMessage(currentPost.message);
}, [currentPost]);
try this:
const [title, setTitle] = React.useState(
currentPost.title && currentPost.title.length ? currentPost.title : ""
);
const [message, setMessage] = React.useState(
currentPost.message && currentPost.message.length ? currentPost.message : ""
);
The currentPost.title condition checks if there is a value in title or it is undefined, if you pass an empty string, then such a condition will return true, in order to check not only for the presence of a value, but also for the presence of some value in it, you can also check the length property, for strings this should work.
You can see the code here:
https://codesandbox.io/s/input-components-o02qg
UPD:
Take a look this example, i make new SandBox, on redux and TS,
https://codesandbox.io/s/serverless-silence-f19lp?file=/src/MyComponent.tsx
P.S. Only I do not understand why create internal state in components, if the state of these components is stored in the Redux store.
P.P.S. Please show more code to understand more

Form validation error in ReactJS not firing first time

My intention was to have a switch, and to validate the appropriate element using an onChange event. It looks like the onChange is sending the previous value of the input field, therefore my validateSearch function is not picking up an error straight away.
Here is an example. You will see that when the age is changed below 18, the onClick event lags behind. How can I prevent this?
https://codesandbox.io/s/vpgzk
Working sandbox
After you've read this code you may have some more questions. Feel free to ask them.
import React, { useState, useEffect } from "react";
const adultAge = 18
const childWarning = 'Under 18'
const emptyStr = ''
function App() {
const [readAge, writeAge] = useState("18");
const [readErrors, writeErrors] = useState({});
useEffect(() => {
if (readAge < adultAge) writeErrors({age: childWarning})
else writeErrors({age: emptyStr})
}, [readAge])
const tooYoung = readErrors.age !== emptyStr
return (
<div>
<form>
<input
type="number"
value={readAge}
onChange={(e) => writeAge(e.target.value)}
/>
<button type="submit" disabled={tooYoung}>Submit</button>
</form>
{tooYoung && <p style={{color: 'red'}}>You are not old enough</p>}
</div>
);
}
export default App;

Why is this onClick function getting triggered when typing on input?

I'm really confused because I'm learning to use hooks and I was working on the useState hook, when I stumbled upon this, the onClick function is getting triggered when I type on the input.
This is the code:
import React, {useState} from 'react'
function Hooks() {
const [count, setInput] = useState("");
return (
<React.Fragment>
<input value={count} onChange={(e) => {setInput(e.target.value)}}>
</input>
<button className="btn" value="See Input!" onClick={console.log(count)}>
Click here!
</button>
</React.Fragment>
)
}
export default Hooks
When I type on the input field, the count gets logged, this is how I fixed it:
import React, {useState} from 'react'
function Hooks() {
const [count, setInput] = useState("");
return (
<React.Fragment>
<input value={count} onChange={(e) => {setInput(e.target.value)}}>
</input>
<button className="btn" value="See Input!" onClick={() => {console.log(count)}}>
Click here!
</button>
</React.Fragment>
)
}
export default Hooks
As you can see, the only thing that changes is that instead of writing the event of onClick as an arrow function, I just write the line of code. I would understand this wouldn't work but why does this get called whenever I write anything in the input?
Thanks in advance!
The onClick function takes a function. In first case you were calling the function by adding parenthesis around. When state was getting updated return was getting rendered which calls the consol.log() on onCLick.
In second case you have passed the function definition and you are not calling it.
The function gets called when user clicks on the button

Resources