I have a react component
import React from 'react';
import classes from './Input.module.css';
import PlayCircleFilledWhiteIcon from '#material-ui/icons/PlayCircleFilledWhite';
export const Input = ({ trackHandler }) => {
const handleTrack = (e) => {
if(e.key === 'Enter') {
trackHandler(e.target.value);
e.target.value = '';
}
}
return (
<>
<div className = {classes.forma}>
<input
type="text"
maxLength = '30'
placeholder = 'Enter tracker name'
onKeyPress = {e => handleTrack(e)}
className = {classes.inputText}
/>
<PlayCircleFilledWhiteIcon className = {classes.btnSubmit}/>
</div>
</>
)
}
Function trackHandler pass the value from input to another component.
I need to pass this value in two ways: by press key 'Enter' on keyboard or click on button. I've realised first way but I need to create both of them.
Thanks in advance for helping.
You can do something like this. Use useState hook to store the input value and create a common function which will be called on button click and on enter key press.
import React, { useState } from "react";
import "./style.css";
const Input = ({}) => {
const [val, setVal] = useState("");
const handleTrack = () => {
if (val.length !== 0) {
// Do something with value
console.log("got this:", val);
}
};
const handleKeyPress = e => {
if (e.key === "Enter") {
handleTrack();
}
};
return (
<div>
<input
value={val}
onChange={e => {
setVal(e.target.value);
}}
onKeyPress={handleKeyPress}
/>
<button
onClick={() => {
handleTrack();
}}
>
Click
</button>
</div>
);
};
export default function App() {
return (
<div>
<Input />
</div>
);
}
Stackblitz: https://stackblitz.com/edit/react-vane9t
You can use useRef as ref property on input.
const inputRef = useRef(null)
Then you get access to input value something like this:
inputRef.target.value
If this not work for first you should log the inputRef to the console which is the exact property what you need.
Related
I have an input in one of my classes which onChange updates some of the properties, according to what the user typed. So I want to call that input, give it a value, then it should go through the onChange method and then get the result from one of the properties. Here is my test case
it("test-input-value-1", async () => {
const { getByTestId } = render(
<>
<Home />
<Try typeracertext="dsa dsa"/>
</>
);
const input = getByTestId("add-word-input");
const inputWord = "For";
userEvent.type(input, 'For')
const userText = await getByTestId("userText");
const typeracertext = getByTestId("typeracertext");
await expect(userText.innerHTML).toBe(inputWord);
});
and here is what I got
I don't have an idea why the result is empty when it has to be changed into the same word "For" that the input has.
EDIT: Here is the JSX Code as requested
Home.js:
const Game = () => {
if (cantType === false) {
return (
<Try
typeracertext={typeracertext}
setWholeText={setWholeText}
setStartTyping={setStartTyping}
setEndTyping={setEndTyping}
setCountWords={setCountWords}
newGame={newGame}
/>
)
}
else {
return (
<input readOnly />
)
}
}
return (
<span data-testid="userText" className="userTextHome">{wholeText}</span><div data-testid="typeracertext">
</div>
<div data-testid="add-word-input2" className="box d">
{Game()}
</div>
...
Try.js:
//here is also the onChange method but it is not needed in this case as it is very long and I have explained what it does in the end (make a property to be equal to the input data)
return (
<div data-testid="add-word-input"><input name="add-word-input" placeholder="Message..." onChange={onChange}></input> </div>
);
You are executing user event on the div element, instead of input element.
Try moving attribute data-testid to input element in Try.js file:
return (
<div><input data-testid="add-word-input" name="add-word-input" placeholder="Message..." onChange={onChange}></input> </div>
);
Working example: Codesandbox
Input.js:
import { useState } from "react";
export const Input = () => {
const [text, setText] = useState();
const handleChange = (e) => {
setText(e.target.value);
};
return (
<>
<div>
<input
data-testid="add-word-input"
name="add-word-input"
placeholder="Message..."
onChange={handleChange}
></input>
</div>
<label data-testId="test-label">{text}</label>
</>
);
};
Input.test.js:
import { render } from "#testing-library/react";
import userEvent from "#testing-library/user-event";
import "#testing-library/jest-dom";
import { Input } from "./Input";
describe("Input", () => {
it("should change", async () => {
const { getByTestId } = render(<Input />);
let input = getByTestId("add-word-input");
expect(input).not.toBe(null);
await userEvent.type(input, "for");
let label = getByTestId("test-label");
expect(label.textContent).toBe("for");
});
});
I am creating a todolist with react and context API. As a default, when item is created "isDone" key of array item is false. When I click the completeAll button, I want to make all task's "isDone" true.
import './FormInput.scss';
import List from '../List/List';
import Footer from '../Footer/Footer';
import {MainContext, useContext} from "../../context";
function FormInput() {
const {taskList, SetTaskList} = useContext(MainContext);
const submitTask = (e) => {
e.preventDefault();
SetTaskList((prev) => [...prev,{"task":e.target.task.value,"isDone":false}])
console.log(e.target.task.value);
}
const CompleteAll = (e) =>{
SetTaskList((prev) => {
const list = prev.map((item) => item.isDone===true)
return{
list
}
})
}
return (
<div className="form-input">
<h1>TODOS</h1>
<div className="form-top">
<button id="completeAll" onClick = { e => CompleteAll(e)}>❯</button>
<form onSubmit = {(e) => submitTask(e)}>
<input type="text" name="task" id="taskInfo" placeholder="What needs to be done?"/>
</form>
</div>
<List/>
{ taskList[0] ? <Footer/> : ""}
</div>
);
}
export default FormInput;
Here is the code. I try to code completeAll function but it set the tasklist to a single true, false value.
You can use spread operator to do it.
const CompleteAll = (e) => {
SetTaskList((prev) => {
return prev.map((item) => ({ ...item, isDone: true }));
});
};
import Layout from "components/Layout"
import { useState } from "react";
export async function getServerSideProps(context) {
const res = await fetch(`${process.env.NEXT_API_URL}/kana-terms/all`)
const data = await res.json()
return {props: {data}}
}
function checkAnswer(event) {
if (event.key === "Enter") {
console.log("Enter key was pressed");
}
}
export default function Hiragana(props) {
const [remainingTerms, setRemainingTerms] = useState(props.data);
return (
<Layout>
<h1>Hiragana</h1>
<div className="bg-light border w-100">
<h2>{remainingTerms[0].hiraganaText}</h2>
<input type="text" onKeyUp={(event) => {checkAnswer(event, )}} />
</div>
</Layout>
)
}
I want to pass the text value of the <input> element to the checkAnswer() function.
How do I do that in React using only function components?
All the answers I can find through Google use class components.
I'm also using Next.js... if that matters.
Put the input value into state, then pass the stateful value into the checkAnswer call:
const [value, setValue] = useState('');
and
<input
type="text"
value={value}
onChange={e => { setValue(e.currentTarget.value); }}
onKeyUp={(event) => {checkAnswer(event, value)}}
/>
I was trying to implement focus for the Submit button with Ref. I wanted to omit refering elements by ID.
import React, { useRef } from 'react'
import PropTypes from 'prop-types'
export const LabelComponent = () => {
const createButton = enableCreateButton()
? <button ref={(input) => { this.createLabelBtn = input }} >Submit</button>
: <button disabled ref={(input) => { this.createLabelBtn = input }} >Submit</button>
const createLabelBtn = useRef();
const focusCreateBtn = (e) => {
if ((e.key === 'Enter') && (newLabel.name !== '')) {
this.createLabelBtn.focus();
}
};
return (
<div className='create-label-container'>
<input type='text'
onKeyDown={(e) => { focusCreateBtn(e) }}
/>
{createButton}
</div>
)
}
It gives following error.
Uncaught TypeError: Cannot set property 'createLabelBtn' of undefined
Uncaught TypeError: Cannot set property 'createLabelBtn' of undefined
What could be the issue here.?
Functional components are instanceless, therefore, no this to bind anything to or call upon. Set the ref prop on the button as so ref={createLabelBtn}, and to set the focus you need to access createLabelBtn.current to get at the current value of the ref.
export const LabelComponent = ({ enableCreateButton }) => {
const createLabelBtn = useRef(null);
const focusCreateBtn = e => {
if (e.key === "Enter") {
createLabelBtn.current.focus();
}
};
return (
<div className="create-label-container">
<input type="text" onKeyDown={focusCreateBtn} />
<button
// upon being focused upon, console log proof
onFocus={() => console.log("Submit Focused!")}
disabled={!enableCreateButton}
ref={createLabelBtn}
>
Submit
</button>
</div>
);
};
Try this
import React, { useRef, useState } from "react";
const LabelComponent = () => {
const [name, setName] = useState("");
const createButton = true ? (
<button
ref={input => {
createLabelBtn.current = input;
}}
>
Submit
</button>
) : (
<button
disabled
ref={input => {
createLabelBtn.current = input;
}}
>
Submit
</button>
);
const createLabelBtn = useRef();
const focusCreateBtn = e => {
if (e.key === "Enter" && name !== "") {
createLabelBtn.current.focus();
}
};
return (
<div className="create-label-container">
<input
type="text"`enter code here`
value={name}
onChange={e => {
setName(e.target.value);
}}
onKeyDown={e => {
focusCreateBtn(e);
}}
/>
{createButton}
</div>
);
};
export default LabelComponent;
I want to grab the value of input inside the array when the button is clicked. How do i pass the input value to the function of button.
Any help would be appreciated. Thanks
import React, { useState, useEffect } from 'react'
export default function Todo(props) {
const [todo,settodo] = useState([]);
function getdata(){
//fetch data
settodo(data);
}
function SaveInput(id){
}
useEffect(() => {
getdata();
},[]);
return (
<React.Fragment>
<div>
{todo.map(function(item, key){
return <div>
<div>{item.name}</div>
<div>
<input type="text" name="inputval" onChange={() => handleChange(e)}>
<button onClick={()=> SaveInput(item.id)}></button>
</div>
</div>
})}
</div>
</React.Fragment>
)
}
You need to send item.id to your handleChange function,
<input type="text" name="inputval" onChange={(e) => handleChange(e,item.id)} />
You handleChange function should,
const handleChange = (e,id) => {
let val = e.target.value;
setInputVal(prevState =>({
...prevState,
[id]:val
}))
}
You must define a state to store input values,
const [inputVal,setInputVal] = useState({});
On the click of button you can access input state,
function SaveInput(id){
console.log(inputVal[id]);
}
Demo
You can save the inputs in a separate useState when the input is being changed, which can be later retrieved easily during the button click event.
Code below is an example and is not tested, but should give you some idea how to proceed.
import React, { useState, useEffect } from 'react'
export default function Todo(props) {
const [todo,settodo] = useState([]);
const [inputVal, setInputVal] = useState({});
function getdata(){
//fetch data
settodo(data);
}
function SaveInput(id){
let inputVal = inputVal[id];
// do other stuff.
}
useEffect(() => {
getdata();
},[]);
return (
<React.Fragment>
<div>
{todo.map(function(item, key){
return <div>
<div>{item.name}</div>
<div>
<input type="text" name="inputval" onChange={(e) => setInputVal({...inputVal, [item.id]: e.target.value })}>
<button onClick={()=> SaveInput(item.id)}></button>
</div>
</div>
})}
</div>
</React.Fragment>
)
}
One common pattern is to use the handleChange(event) function on input to set a state with the current value.
const [input,setInupt] = useState("");
function handleChange(event) {
setInput(event.target.value)
}
and when the button is clicked, you can use the value of the input state to pass on
<button onClick={()=> console.log(input))}>
First of all, If you are having an onChange method then you must have a value for that input as well or else it will display a warning for "uncontrolled input" and that input box is of no use to you unless you provide a value to it.
Secondly, you should use a state for the values of those input boxes and then you can access the values of input in the save button click function. Here is the example of how you can do it.
import React from 'react'
export default class Todo extends React.Component {
constructor(props) {
super(props);
this.state = {
inputIDs: {}
}
}
SaveInput = id => {
console.log("input value:", this.state[id]);
};
handleChange = (e, id) => {
this.setState({[id]: e.target.value});
};
render() {
const {inputIDs} = this.state;
const todo = [
{id: 1, val: "abc", name: "lorem"},
{id: 2, val: "xyz", name: "Ipsum"}
];
let todos = todo.map((item, key) => {
return <div key={key}>
<div>{item.name}</div>
<div>
<input type="text" value={this.state[item.id]} onChange={(e) => this.handleChange(e, item.id)}/>
<button onClick={() => this.SaveInput(item.id)}>Click Me!</button>
</div>
</div>
});
return (
<React.Fragment>
{todos}
</React.Fragment>
)
}
}