How to select element value without attaching state - reactjs

For some reason, the showNumber variable gets updated upon submit click, but not in in return statement. Anyhow, is there a better way to write this? Keep in mind the line in should not simply change with every input onChange but rather, only after a submit button click. I suppose one solution would be to select input via refs, but I'm hoping there's a better way. Please help.
function App() {
const [number, setNumber] = useState(1);
let showNumber = 0;
const rerender = () => {
showNumber = number;
}
const handleChange = (e) => { setNumber(e.target.value) }
return (
<header>
<input type='number' onChange={handleChange} />
<button type='submit' onClick={rerender}>submit</button>
<p>the number is {showNumber}</p>
</header>
);
}
export default App;

By Wrapping the form tag you can achieve, so when you click on the submit only it will get the form values and you can fetch the values by using name attribute
Sample Code
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
function App() {
const [number, setNumber] = useState(1);
const rerender = e => {
e.preventDefault();
const data = new FormData(e.target);
setNumber(data.get("number"));
};
return (
<header>
<form onSubmit={rerender}>
<input type="number" name="number" />
<button>Submit</button>
</form>
<p>the number is {number}</p>
</header>
);
}
export default App;
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Working codesandbox

Related

How do I transfer the value of the inputs from one page to another?

On one page I have two inputs and a button, after clicking on the button goes to the second page, how do I get data from the inputs on the second page?
navigate(path, { state: { input_value: value }}) ?
index.js
import './App.css';
function App() {
return (
<div className='App'>
<header className='App-header'>
<input type='text' placeholder='Name'/>
<input type='text' placeholder='Surname'/>
<button type='submit'>Send</button>
</header>
</div>
);
}
export default App;
getData.js
export const getData = () => {
return (
<div>
<h1>Name:</h1>
<h1>Surname:</h1>
</div>
)
};
You can have a state variable in the App component and then pass the state as a prop to GetData:
import './App.css';
import { useState, useRef } from "react";
function App() {
const nameInput = useRef(null);
const surNameInput = useRef(null);
const [fullName, setFullName] = useState({
name: "",
surName: ""
});
const sendData = () => {
// you can do some input validation here
setFullName({
name: nameInput.current.value,
surName: surNameInput.current.value,
});
}
return (
<div className='App'>
<header className='App-header'>
<input type='text' placeholder='Name'
ref={nameInput}
/>
<input type='text' placeholder='Surname'
ref={surNameInput}
/>
<button onClick={sendData}>Send</button>
</header>
<!-- some more markup here -->
</div>
);
}
export default App;
Here's how you pass your data to GetData component:
<GetData fullName={fullName} />
And then in your GetData component you get the passed props and display them:
export const GetData = (fullName) => {
return (
<div>
<h1>Name: {fullName.name}</h1>
<h1>Surname: {fullName.surName}</h1>
</div>
)
};
You can read more about hooks like useState and useRef here
So this might be Part 1 of an answer.
Taking the code you have, I've put the fields in form tag, then called handleSubmit from the built in onSubmit that is called when you click a button of type='submit' inside a form.
The values is taken from the event parameter (e) of the onSubmit (you can identify these by the 'name' attribute on the input tags) and then I am using useState hook to store the two values.
This would be where part one ends. You have the data, and you can see how it is passed to the GetDate component (the deconstructed props {name, surname} that are passed in.
From there, you should follow the documentation for your chosen router and, if you run into trouble, post the code you've tried and I can continue to help.
https://v5.reactrouter.com/web/guides/quick-start
import React, { useState } from 'react';
import { GetData } from './GetData';
export function App() {
const [theName, setTheName] = useState('');
const [theSurname, setTheSurname] = useState('');
const handleSubmit = (e) => {
setTheName(e.target.name.value);
setTheSurname(e.target.surname.value);
e.preventDefault();
}
return (
<div className='App'>
<header className='App-header'>
<form onSubmit={handleSubmit}>
<input type='text' placeholder='Name' name='name'/>
<input type='text' placeholder='Surname' name='surname'/>
<button type='submit'>Send</button>
</form>
</header>
<GetData name={theName} surname={theSurname} />
</div>
);
}
export default App;
Here is a component like your getData function.
I've added it to the App component, just so you can see the values being displayed, but for what you are looking for, you will need to read the documentation for react-router-dom
import React from "react";
export const GetData = ({name, surname}) => {
return (
<div>
<h1>Name:{name}</h1>
<h1>Surname:{surname}</h1>
</div>
)
};

How do I console.log state with forms in React hooks?

When I submit text in the form, I want to be able to see that text in state via console.log.
I added a console.log to trigger directly after state has been added, but I don't see anything in my console, what am I doing wrong?
What I would like to see: I submit "test1" then I submit "test2", I then want to see in my console "test1, test2" in state.
import React, { useState } from 'react';
import './App.css';
function App() {
return (
<div>
<Field />
</div>
);
}
function Field(){
const [toDoItem, setToDoItem] = useState('');
const addToDoItem = (event) => {
setToDoItem(event.target.value), function(){
console.log(toDoItem)
}
}
return (
<form>
<input type="text" value={toDoItem} onChange={addToDoItem}/>
<input type="submit" value="Add" />
</form>
);
}
export default App;
You can log the change in states using a useEffect
I'd suggest making a helper function if you tend to do it often:
function useLog(name, property) {
useEffect(() => console.log(name, property), [name, property]);
}
Which you'd use as follows:
useLog('toDoItem', toDoItem);
I set up an example where the toDoItem is logged as it changes and when you submit, it also logs the change in a todoItems array
const { useState, useEffect } = React;
function useLog(name, property) {
useEffect(() => console.log(name, property), [name, property]);
}
function App() {
return (
<div>
<Field />
</div>
);
}
function Field() {
const [toDoItem, setToDoItem] = useState('');
useLog('toDoItem', toDoItem);
const [todos, setTodos] = useState([]);
useLog('todos', todos);
const changeTodo = (event) => {
setToDoItem(event.target.value);
};
const addTodoItem = (event) => {
event.preventDefault();
setTodos((prev) => prev.concat(toDoItem));
setToDoItem('');
};
return (
<form onSubmit={addTodoItem}>
<input type="text" value={toDoItem} onChange={changeTodo} />
<input type="submit" value="Add" />
</form>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"/>
useState doesn't provide callback after setting value. You can use useEffect instead.
React.useEffect(() => {
console.log(toDoItem);
}, [toDoItem]);
EDIT
It seems like that you want to get toDoItem value on submit. The problem is that the page is reloaded when form is submitted. You can use event.prefentDefault() to stop refreshing on form submission.
<form onSubmit={onSubmit}>
const onSubmit = (event) => {
event.preventDefault()
console.log(toDoItem)
}
import React, { useState } from 'react';
import './App.css';
function App() {
return (
<div>
<Field />
</div>
);
}
function Field(){
const [toDoItem, setToDoItem] = useStateWithCallback('', toDoItem => {
console.log(toDoItem);
});
const addToDoItem = (event) => {
setToDoItem(event.target.value);
}
return (
<form>
<input type="text" value={toDoItem} onChange={addToDoItem}/>
<input type="submit" value="Add" />
</form>
);
}
export default App;

check state of another functional component

i am new to react, i want to call the state of an outside function, for example :
export default function Child() {
const [succeeded, setSucceeded] = useState(false);
}
export default function Parent() {
if(Child.succeeded){
// do the following
}
}
i know that props are used for const objects only, and i don't want to merge both functions in a signle one to keep things organised, i would like to check for child's state to do the next step, or to callback the parent function with the new state to notify it. is there any way to do it ? Thanks a lot for your time.
Another approach is that you can use the useRef, which is very handy in some cases.
import React, {useState} from "react";
export default function Child({nameRef}) {
const [name, setName] = useState('');
React.useEffect(() => {
nameRef.current = name;
}, [name]);
return (
<>
<input nameRef={nameRef} type="text" onChange={event => setName(event.target.value)} />
</>
);
}
import React, { useState, useRef } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
import Child from './Child';
function App() {
let [name, setName] = useState("Nate");
let nameRef = useRef();
const submitButton = () => {
console.log(nameRef.current);
};
return (
<div className="App">
<p>{name}</p>
<div>
<Child nameRef={nameRef} />
<button type="button" onClick={submitButton}>
Submit
</button>
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Debouncing and Setting text data not happening at the same time

I am trying to use the debounce feature as well as the ability to retain the value of textbox that I typed but it's not happening for some reason. If I comment out setMyval(e.target.value); on line #20 then the debounce works without any issue but the value I type does not show up. Whereas if I uncomment it, then the value shows in the textbox but debounce feature does not work (meaning there are multiple console logs). Please if someone can tell me why is this happening and how can I make it work, it would help me.
Below is my reactjs code:
// App.js
import React, { useState } from "react";
import ReactDOM from "react-dom";
import debounce from "./debounce";
function App() {
const [myval, setMyval] = useState("");
const handleChange = debounce(() => {
console.log("This log msg should be debounced");
}, 2000);
return (
<div className="App">
<input
type="text"
value={myval}
onChange={e => {
setMyval(e.target.value);
handleChange(e.target.value);
}}
/>
<button onClick={() => setMyval("my new value")}>Change Value</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
// debounce.js
export default function debounce(fn, wait) {
let timer;
return function() {
const context = this;
const args = arguments;
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(context, args);
}, wait);
};
}
Code at https://codesandbox.io/s/affectionate-wind-0ef3y
What I expect:
If I type a word in the textbox, the value should remain in the textbox.
Debounce should work i.e. few console logs should appear.
If I click on the "Change Value" button, it should update the value "my new value" in the textbox.
https://codesandbox.io/s/sharp-turing-w4hu9
Your debounce doesnt work, because your debounce function gets redeclared on every component update. Use useCallback to keep the reference the same and avoid reinitialization
import React, { useState, useCallback } from "react";
import ReactDOM from "react-dom";
import debounce from "./debounce";
import "./styles.css";
function App() {
const [myval, setMyval] = useState("");
const handleChange = useCallback(debounce(() => {
console.log("This log msg should be debounced");
}, 2000), []);
return (
<div className="App">
<input
type="text"
value={myval}
onChange={e => {
setMyval(e.target.value);
handleChange(e.target.value);
}}
/>
<button onClick={() => setMyval("my new value")}>Change Value</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Custom react hook to for text field seems to be broken

I have been trying to come up with a custom hook to make the textfield configurable, i.e pass the set of data to a custom hook which would give me the text field that needs to be used.
The text field using the hook is being rendered as expected but I do not understand why this approach is breaking the input created using the custom hook. After every keystroke the input is losing focus and is not working as the other input that is using useState directly. It would be great if someone can explain what is going wrong and what I failed to understand.
App.js
import React, { useState } from "react";
import ReactDOM from "react-dom";
import useTextFieldBroken from "./useTextFieldBroken";
import "./styles.css";
function App() {
const [notBrokenValue, notBrokenSetValue] = useState("");
const [TextFieldBrokenInputOne] = useTextFieldBroken(
"brokenOne",
"Broken Input One",
""
);
const notBrokenOnChange = e => {
notBrokenSetValue(e.target.value);
};
return (
<div>
<label htmlFor="notBroken">
<h3>Not Broken Input</h3>
<input
id="notBroken"
onChange={notBrokenOnChange}
value={notBrokenValue}
/>
</label>
<TextFieldBrokenInputOne />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
customHook.js
import React, { useState } from "react";
const useTextFieldBroken = (id, label, initialValue = "") => {
const [value, setValue] = useState(initialValue);
const handleChange = e => {
setValue(e.target.value);
};
const TextField = () => {
console.log("Rendered the input field");
return (
<label htmlFor={id}>
<h3>{label}</h3>
<input
type="text"
name={id}
id={id}
onChange={handleChange}
value={value}
/>
</label>
);
};
return [TextField, value, setValue];
};
export default useTextFieldBroken;
https://codesandbox.io/s/4xj382vj40
Your input is losing focus because you're completely re-rendering the tree that creates it on each change.
The good news is that you don't need a hook to do this, just convert your hook into a functional component instead:
App.js
import React, { useState } from "react";
import ReactDOM from "react-dom";
import TextFieldBroken from "./useTextFieldBroken";
import "./styles.css";
function App() {
const [notBrokenValue, notBrokenSetValue] = useState("");
const notBrokenOnChange = e => {
notBrokenSetValue(e.target.value);
};
return (
<div>
<label htmlFor="notBroken">
<h3>Not Broken Input</h3>
<input
id="notBroken"
onChange={notBrokenOnChange}
value={notBrokenValue}
/>
</label>
<TextFieldBroken label="Previously Broken" id="previously-broken" />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
customHook.js
import React, { useState } from "react";
const TextFieldBroken = ({ id, label, initialValue = "" }) => {
const [value, setValue] = useState(initialValue);
const handleChange = e => {
setValue(e.target.value);
};
return (
<label htmlFor={id}>
<h3>{label}</h3>
<input
type="text"
name={id}
id={id}
onChange={handleChange}
value={value}
/>
</label>
);
};
export default TextFieldBroken;

Resources