Lost input focus on hooks function state change - reactjs

When i define the hooks state in the parent function i lost input field focus on first key press. I need the state definition in the root function.
import React, { useState } from 'react'
function Test1(props) {
const [test, setTest] = useState({value1: "", value2:""});
const Test = () => {
const handleChange= (e) => {
const _test = {...test, [e.target.name]: e.target.value}
setTest(_test)
}
return (
<div style={{ margin: "200px" }}>
<input name="value1" value={test["value1"]} onChange={handleChange}></input>
<input name="value2" value={test["value2"]} onChange={handleChange}></input>
<button onClick={() => console.log(test)}>Console.Log</button>
</div>
)
}
return (
<Test />
);
}
export default Test1;
But if I move the state definition in to the child function it works.
import React, { useState } from 'react'
function Test1(props) {
const Test = () => {
const [test, setTest] = useState({value1: "", value2:""});
const handleChange= (e) => {
const _test = {...test, [e.target.name]: e.target.value}
setTest(_test)
}
return (
<div style={{ margin: "200px" }}>
<input name="value1" value={test["value1"]} onChange={handleChange}></input>
<input name="value2" value={test["value2"]} onChange={handleChange}></input>
<button onClick={() => console.log(test)}>Console.Log</button>
</div>
)
}
return (
<Test />
);
}
export default Test1;
So! Why is this happening and how can I get over it?

I have been seeing this pattern a lot where people nest components in methods in components. It may be an opinion, but I feel like this may not be a great pattern.
I would abstract the one component function and pass the props down to the 2nd. something like this
const Test = ({test, setTest}) => {
const handleChange= (e) => {
const _test = {...test, [e.target.name]: e.target.value}
setTest(_test)
}
return (
<div style={{ margin: "200px" }}>
<input name="value1" value={test["value1"]} onChange={handleChange}></input>
<input name="value2" value={test["value2"]} onChange={handleChange}></input>
<button onClick={() => console.log(test)}>Console.Log</button>
</div>
)
}
function Test1(props) {
const [test, setTest] = useState({value1: "", value2:""});
return (
<Test test={test} setTest={setTest} />
);
}
export default Test1;

Related

Trying to get form values in Typescript React but HTML tag is returned

I am trying to get the form value to my useRef hook but what gets returned is the form HTML tage
I tried inferring the HTMLFormElement type inside the chevrons and null as the current value of my useRef hook.
Here is the rest of the the file: NameTodo.tsx
import React, { FC, ChangeEvent, FormEvent, useState, useRef } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { addTodoName, toggleNaming } from '../todoReducer';
import { RootState } from '../todoStore';
const NameTodo: FC = () => {
const dispatch = useDispatch();
const selector = useSelector((state: RootState) => { return state.todo })
const [input, setInput] = useState<any>('');
// Current value returns the HTML form tag itself instead of its value
const ref = useRef<HTMLFormElement>(null);
const exitNamingMenu = (): void => {
dispatch(toggleNaming(false));
}
const handleInput = (event: ChangeEvent<HTMLInputElement>): void => {
event.preventDefault();
setInput(event.target.value);
}
const submitTodoName = (event: FormEvent<HTMLFormElement>): void => {
event.preventDefault();
//dispatch(addTodoName(input));
console.log(ref.current);
dispatch(toggleNaming(false));
}
return (
<div className='position-absolute start-50 mt-4 translate-middle bg-success' style={{ width: '18vw', height: '20vh' }}>
<span className='position-absolute end-0'>
<button className='text-white bg-success bg-opacity-10 border border-0' onClick={exitNamingMenu}>x</button>
</span>
<form className='d-flex flex-column' ref={ref} onSubmit={submitTodoName}>
<p className='text-white text-center' style={{ marginTop: '3vh' }}>Enter the name of your todo-list</p>
<input type='text' name="input" className='p-1 rounded bg-light border-0 ms-4' style={{ width: '15vw' }} onChange={handleInput}></input>
<button type="submit" className='btn btn-light mt-3' style={{ width: "6vw", marginLeft: '6vw' }}>Enter</button>
</form>
</div>
)
}
export default NameTodo
There are no errors showing up so aside from this, everything else is fine...
Thank you in advance for the responses :)
You can use ref on input elements to get the value, form doesn't have value. For complex forms, you can use libraries such as Formik and React Hook Form.
Both of them are great.
you can check the example in the link below:
https://codesandbox.io/s/react-template-forked-loq0yn?file=/src/index.js
And if you want to handle it with the state:
function App() {
const [inputValue, setInputValue] = useState('');
const handleSubmit = () => {
console.log('Value ', inputValue);
};
return (
<div className="App">
<form onSubmit={handleSubmit}>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button type="submit">Submit</button>
</form>
Value: {inputValue}
</div>
);
}

How can I send the data to the parent component by click on the button in React?

My question is how can I send the input value to the parent component by clicking on the button? Because now if I type something in the input it shanges the value instantly, I want it to do after I click on the button.
Currently I am using that method:
const FormInput = ({setIpAddress}) => {
return (
<div className="formInput">
<form className="form_container" onSubmit={e => {e.preventDefault();}}>
<input type="text" id="input" onChange={(e) => setIpAddress(e.target.value)} required={true} placeholder="Search for any IP address or domain"/>
<button type="submit" className="input_btn">
<img src={arrow} alt="arrow"/>
</button>
</form>
</div>
);
};
export default FormInput
You can pass an onClick callback function to the child component. When this function is called it will trigger a rerender in the child.
Example:
Parent:
const handleClick = (value) => {
//set the state here
}
<ChildComponent onClick={handleClick} />
Child:
<button type="submit" className="input_btn" onClick={(value) => props.onClick?.(value)}>
In your case you need to get rid of the onChange in your input tag:
parents:
function App() {
const [ipAddress, setIpAddress] = useState("");
const url = `${BASE_URL}apiKey=${process.env.REACT_APP_API_KEY}&ipAddress=${ipAddress}`;
useEffect(() => {
try {
const getData = async () => {
axios.get(url).then((respone) => {
setIpAddress(respone.data.ip);
});
};
getData();
} catch (error) {
console.trace(error);
}
}, [url]);
const handleClick = (event) => {
setIpAddress(event.target.value)
}
return (
<div className="App">
<SearchSection onClick={handleClick} />
</div>
);
}
const SearchSection = ({onClick}) => {
return (
<div className="search_container">
<h1 className="search_heading">IP Address Tracker</h1>
<FormInput onClick={onClick}/>
</div>
);
};
Child
const FormInput = ({onClick}) => {
return (
<div className="formInput">
<form className="form_container" onSubmit={e => {e.preventDefault();}}>
<input type="text" id="input" required={true} placeholder="Search for any IP address or domain"/>
<button type="submit" className="input_btn" onClick={(e) => onClick(e}>
<img src={arrow} alt="arrow"/>
</button>
</form>
</div>
);
};
Thank you for your answer, but I don't really get it, bcs my parent component has no paramter, sorry I am new in react.
This is my parent component where I am fetching the data and I want to update the ipAdress when I click on the button which is in the FormInput component. So the SearchSection is the parent of the FormInput.
function App() {
const [ipAddress, setIpAddress] = useState("");
const url = `${BASE_URL}apiKey=${process.env.REACT_APP_API_KEY}&ipAddress=${ipAddress}`;
useEffect(() => {
const getData = async () => {
axios.get(url).then((respone) => {
setIpAddress(respone.data.ip)
...
getData();
}, [url]);
return (
<div className="App">
<SearchSection setIpAddress={setIpAddress} />
</div>
);
}
I hope it's enough :)
const SearchSection = ({setIpAddress}) => {
return (
<div className="search_container">
<h1 className="search_heading">IP Address Tracker</h1>
<FormInput setIpAddress={setIpAddress}/>
</div>
);
};
function App() {
const [ipAddress, setIpAddress] = useState("");
const url = `${BASE_URL}apiKey=${process.env.REACT_APP_API_KEY}&ipAddress=${ipAddress}`;
useEffect(() => {
try {
const getData = async () => {
axios.get(url).then((respone) => {
setIpAddress(respone.data.ip);
});
};
getData();
} catch (error) {
console.trace(error);
}
}, [url]);
return (
<div className="App">
<SearchSection setIpAddress={setIpAddress} />
</div>
);
}

How to pass current value of state variable to function in react?

I have a state variable count which changes when I update my form input .
Also I want to pass the current value of count to another function all but I am unable to do so as it still gets the initial value of count and not the updated one.
import React, { useState } from "react";
import SingleColor from "./SingleColor";
import Values from "values.js";
function App() {
const [color, setColor] = useState("");
const [error, setError] = useState(false);
const [count, setCount] = useState(10); ////////Here
const [list, setList] = useState(new Values("#f15025").all(10));
const handleSubmit = (e) => {
e.preventDefault();
try {
console.log("Before: " + count);
let colors = new Values(color).all(count); /////////Here
console.log("After: " + count);
setList(colors);
} catch (error) {
setError(true);
console.log(error);
}
};
return (
<>
<section className="container">
<h3>color generator</h3>
<form onSubmit={handleSubmit}>
<input
type="text"
value={color}
placeholder="#f15025"
onChange={(e) => setColor(e.target.value)}
className={`${error ? "error" : null} `}
/>
<input
type="number"
value={count}
onChange={(e) => {
setCount(e.target.value); /////////Updating
}}
style={{ marginLeft: "5px" }}
/>
<button className="btn" type="submit">
submit
</button>
</form>
</section>
<section className="colors">
{list.map((color, index) => {
// console.log(color);
return (
<SingleColor
key={index}
{...color}
index={index}
hexColor={color.hex}
/>
);
})}
</section>
</>
);
}
export default App;
How can I pass the current value of count to the function all ?

trying to delete from an API using axios and React hooks

Hello im trying to delete a Booking from an api using an input with the ID . obviously something is wrong. I tried to convert my class to a HOC and i just cant get it to work. Right now i cant even type in the textbox .
I know i have severals errors but i dont know how to solve them would appreciate some help. the only relevant parts in the HTML is the form.
const DeleteBooking = () => {
const [ModalIsOpen, SetModalIsOpen] = useState(false); // set false if closen when open
const [booking, setDelete] = useState([]);
const handleChange = (e) => {
setDelete({ [e.target.name]: e.target.value });
};
useEffect((UserIdInput) => {
const bookingId = UserIdInput.target.elements.bookingId.value;
Axios.delete(`https://localhost:44366/api/Products/${bookingId}`) // change api key
.then((response) => {
console.log(response);
setDelete(response.data);
});
}, []);
return (
<>
<div className="App-header">
<button onClick={() => SetModalIsOpen(true)}>Radera bokning</button>
</div>
<Modal
isOpen={ModalIsOpen}
onRequestClose={() => SetModalIsOpen(false)}
style={{
overlay: {
background:
"linear-gradient(-500deg, #ee7752, #6e1b3b, #0c495f, #000000)",
},
content: {
color: "black",
textAlign: "center",
},
}}
>
<div>
<h1>Radera Bokning</h1>
<p style={{ marginTop: "20px" }}>
Vänligen ange ditt bokningsNummer för att radera din bokning
</p>
<form onSubmit={() => setDelete}>
<input
onChange={handleChange}
type="text"
name=" bookingId"
placeholder="BokningsID"
value="bookingId"
></input>
<button type="submit"></button>
</form>
<button
style={{ marginTop: "100px" }}
onClick={() => SetModalIsOpen(false)}
>
Tillbaka
</button>
</div>
</Modal>
</>
);
};
export default DeleteBooking;
Here is an incredibly simple example (working sandbox) that you can build upon:
import Axios from "axios";
import React, { useState } from "react";
// => Component Code
// -> This component will be used to delete posts
export default function App() {
// => State
const [readPostId, writePostId] = useState("");
const [readStatus, writeStatus] = useState("");
// => Handlers
const updatePostId = (e) => writePostId(e.target.value);
const deletePost = async (e) => {
e.preventDefault();
try {
await Axios.delete(`${API_ENDPOINT}/${readPostId}`);
writeStatus("Post successfully deleted");
setTimeout(() => writeStatus(""), 3000);
} catch (err) {
writeStatus("Post deletion failed");
}
};
return (
<div>
<h1>Delete Posts Page</h1>
<h2>Enter your Post ID:</h2>
<em>Press 'Delete' without entering a number to cause an error</em>
<form onSubmit={deletePost}>
<input onChange={updatePostId} value={readPostId} />
<input type="submit" value="Delete" />
</form>
{readStatus && <p>{readStatus}</p>}
</div>
);
}
// => Support Code
const API_ENDPOINT = "https://jsonplaceholder.typicode.com/posts";

Input not re-rendering onChange with hooks

When typing and logging the input e.target.value, I get the default value + the last key stroke, but nothing re-renders. I guess that React doesn't recognize that the state changed, but I'm having a problem finding out the correct way to do this.
This is the code in question:
const [text, setText] = useState(task.text);
console.log(text);
const handleInputChange = (e) => {
setText(e.target.value);
};
const taskInput = (
<form>
<input type='text' value={text} onChange={handleInputChange} />
</form>
);
And the full file:
import React, { useContext, useState } from "react";
import { TaskContext } from "../context/TaskState";
const Task = ({ task }) => {
const { deleteTask } = useContext(TaskContext);
const { changeStatus } = useContext(TaskContext);
const taskText = (
<div
className='task-text'
onClick={() => changeStatus({ ...task, done: !task.done })}
style={task.done ? { textDecoration: "line-through" } : null}
>
{task.text}
</div>
);
const [text, setText] = useState(task.text);
console.log(text);
const handleInputChange = (e) => {
setText(e.target.value);
};
const taskInput = (
<form>
<input type='text' value={text} onChange={handleInputChange} />
</form>
);
const [option, setOption] = useState(taskText);
return (
<div className='task-container'>
<button className='task-edit' onClick={() => setOption(taskInput)}>
edit
</button>
<button className='task-delete' onClick={() => deleteTask(task.id)}>
x
</button>
{option}
</div>
);
};
export default Task;
I'am using global state for the rest of the app and reducers.
I think, onChange in your input might cause this error. Try replacing this:
onChange={handleInputChange}
with this:
onChange={(e) => handleInputChange(e)}
e object might be not passed to your method.
Please try wrapping your taskInput value in useMemo with dependency text as when you store JSX as variable during re-render they are refering to the previous value as they don't know the variable they used have value changed.
import React, { useMemo, useContext, useState } from "react";
const taskInput = useMemo(() => (
<form>
<input type='text' value={text} onChange={handleInputChange} />
</form>
), [text]);
The problem was the way I passed option inside the jsx.
I made the option state a boolean, converted taskText and taskInput to functions and passed option conditionally inside the jsx.
import React, { useContext, useState } from "react";
import { TaskContext } from "../context/TaskState";
const Task = ({ task }) => {
const { deleteTask } = useContext(TaskContext);
const { changeStatus } = useContext(TaskContext);
const taskText = () => {
return (
<div
className='task-text'
onClick={() => changeStatus({ ...task, done: !task.done })}
style={task.done ? { textDecoration: "line-through" } : null}
>
{task.text}
</div>
);
};
const [text, setText] = useState(task.text);
console.log(text);
const handleInputChange = (e) => {
setText(e.target.value);
};
const taskInput = () => {
return (
<form>
<input type='text' value={text} onChange={handleInputChange} />
</form>
);
};
const [option, setOption] = useState(true);
return (
<div className='task-container'>
<button className='task-edit' onClick={() => setOption(!option)}>
edit
</button>
<button className='task-delete' onClick={() => deleteTask(task.id)}>
x
</button>
{option ? taskText() : taskInput()}
</div>
);
};
export default Task;

Resources