I'm new to react and I'm trying to edit an input field after I prefilled its value with an object value from my database, so what should I put on onChange if value is value={data.info}? because I cannot type or change the initial value. I've watched a lot of tutorials but this. and props are very confusing to me
import React, { useState } from 'react';
import { useParams } from 'react-router-dom';
import useAsync from '../useAsync';
export default function Details() {
const url = 'https://..';
const { index } = useParams();
const { data } = useAsync(url + index);
const [state, setState] = useState(false);
const showForm = () => {
return (
<div>
<form>
<input type="text" value={data.info} onChange={} />
</form>
</div>
)
}
return (
<div className="details" >
{data && <p key={index}>{data.info}</p>}
<button onClick={() => setState({ showForm: true })}>Edit</button>
{state.showForm ? showForm() : null}
</div>
)
}
You can add "default value" to your state. So you can move the data value to your useState(false) so useState(data)
import React, { useState } from "react";
const App = () => {
const [formInput, setFormInput] = useState(""); // You can add your "data" as default value
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>formInput Value {formInput}</h2>
<input
type="text"
value={formInput}
onChange={(e) => setFormInput(e.target.value)} // You need to set the state with the onchange value from the input
/>
</div>
);
};
export default App;
Link to CodeSandbox
Related
I am try to add search feature to an existing lists of robot names.
In order to do so I am trying to useState hooks. I have an App component and Header component which has the input tag for search field.
Error I am getting is 'InputEvent' is assigned a value but never used.
Below is the code for App component (main component).
import "./App.css";
import Header from "./Header";
import Robo from "./Robo";
import { robots } from "./robots";
import { useState } from "react";
function App() {
const [query, setQuery] = useState("");
const InputEvent = (e) => {
const data = e.target.value;
setQuery(data);
const extraction = robots
.filter((curElem, index) =>
robots[index].name.toLowerCase().includes(query)
)
.map((curElem, index) => {
return (
<Robo
key={robots[index].id}
id={robots[index].id}
name={robots[index].name}
email={robots[index].email}
/>
);
});
return (
<div className="App">
<Header query={query} InputEvent={InputEvent} />
<div className="robo-friends-container">{extraction};</div>
</div>
);
};
}
export default App;
Child component
import React from "react";
import "./header.css";
const Header = ({ query, InputEvent }) => {
return (
<>
<div className="headerText">ROBO FRIENDS</div>
<div>
<input
type="text"
id="lname"
name="lname"
placeholder="Search"
value={query}
onChange={InputEvent}
/>
</div>
</>
);
};
export default Header;
Here is my answer in stackblitz app
https://stackblitz.com/edit/stackoverflow-robots-filter?file=App.tsx,Robo.tsx,Header.tsx,robots.ts
I have altered the code a bit.. you can fork the project and play with it..
You can add debounce option to your input, which prevents unwanted re-renders
Adding the changes:
function App() {
const [query, setQuery] = useState(undefined);
const [filteredRobots, setFilteredRobots] = useState([]);
useEffect(() => {
console.log(query);
const filteredRobots = robots.filter((robot) => {
return robot.name.includes(query);
});
if (filteredRobots.length) {
setFilteredRobots(filteredRobots);
}
}, [query]);
const onQueryChange = (e) => {
const data = e.target.value;
setQuery(data);
};
const renderRobots = () => {
if (!query || !query.length) {
return <p>{'Search to find Robots'}</p>;
}
if (filteredRobots && filteredRobots.length && query && query.length) {
return filteredRobots.map((filteredRobot) => (
<Robo
key={filteredRobot.id} //id is unique key in your data
name={filteredRobot.name}
id={filteredRobot.id}
email={filteredRobot.email}
/>
));
}
return <p>{'No Robots Found'}</p>;
};
return (
<div className="App">
<Header query={query} InputEvent={onQueryChange} />
{renderRobots()}
</div>
);
}
Problems in your code:
Const InputChange is a function that can be used as prop for any React component .. but you have added InputChange inside the InputChange named function itself which is incorrect
Extraction is a jsx variable which is created from Array.filter.. on each item, filter passes a item[index] to the filter function.. you dont want to do robots[index].name.toLowerCase().includes(query).. instead you could have done curElem.name.toLowerCase().includes(query) and same applies for Array.map
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>
)
};
I've been working on variations of this problem for a while now. Basically, I have a child component that can update existing data. It updates data with no problems and the parent re-renders accordingly. The child component doesn't re-render though. So, on advice given on this site, I've tried lifting the state. I'm passing down props down to the two child components I'm running. My problem is the "EditStudent" component. I can't seem to destructure/get the "setStudent" function that's being passed down from the parent component so I'm getting a "setStudent is not a function error" no matter how I try to call this function. Any advice is greatly appreciated as it's been driving me slowly insane on how to figure this out.
Here's the code I've been working with so far.
Parent component "StudentList"
import React, { useState } from "react";
import { useQuery } from "#apollo/client";
import { getStudents } from "../queries";
import StudentDetails from "./StudentDetails";
import DeleteStudent from "./DeleteStudent";
import EditStudent from "./EditStudent";
const StudentList = () => {
const [selectedStudent, setSelectedStudent] = useState("");
const { loading, error, data } = useQuery(getStudents);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error!</p>;
const handleClick = (student)=> {
//console.log(student)
setSelectedStudent(student);
};
let filteredStudents = [];
//console.log(data.students)
for(let i = 0; i < data.students.length; i++){
//console.log(data.students[i].class.name)
if(data.students[i].class.name === "1FE1"){
//console.log(data.students[i].name)
filteredStudents.push(data.students[i])
}
}
console.log(selectedStudent.id);
return (
<div>
<ul id="student-list">
{data.students.map((student) => (
<li key={student.id} onClick={(e) => handleClick(student)}>{student.name}</li>
))}
</ul>
{
selectedStudent ? <div>
<StudentDetails student={selectedStudent} setStudent={setSelectedStudent}/>
</div>
: <p>No Student Selected</p>
}
</div>
);
};
export default StudentList;
This is "StudentDetails" - a component receiving the "studentDetails" prop and also has two other components nested inside - "DeleteStudent" and "EditStudent"
import React from "react";
import { useEffect, useState } from "react";
import { getStudentQuery } from "../queries";
import { useQuery } from "#apollo/client";
import DeleteStudent from "./DeleteStudent"
import EditStudent from "./EditStudent";
const StudentDetails = ( selectedStudent )=> {
const {setStudent} = selectedStudent;
console.log(selectedStudent)
//const [astudent, setStudent] = useState(props)
return (
<div id="student-details" >
<h2>Name: {selectedStudent.student.name}</h2>
<h3>Age: {selectedStudent.student.age}</h3>
<h3>Class: {selectedStudent.student.class.name}</h3>
<h3>Test 1 Score: {selectedStudent.student.test1}</h3>
<EditStudent student={selectedStudent} setstudent={setStudent}/>
<DeleteStudent student={selectedStudent} setter={setStudent} />
</div>
)
}
export default StudentDetails;
Finally, here is the "EditStudent" component which is causing me so many problems (can't get the setStudent function from the parent to change the state)
import React, { useEffect, useState } from "react";
import { useMutation } from "#apollo/react-hooks";
//import { getStudents } from "../queries";
import StudentDetails from "./StudentDetails";
import { editStudentMutation, getStudentQuery, getStudents } from "../queries/index";
const EditStudent = ( setStudent ) => {
const { setStudent } = selectedStudent;
console.log(props)
const [name, setName] = useState();
const [age, setAge] = useState();
const [test, setTest] = useState();
const [editStudent] = useMutation(editStudentMutation);
return (
<form id="edit-student"
onSubmit={(e) => {
e.preventDefault();
editStudent({
variables: {
id: selectedStudent.student.student.id,
name: name,
age: age,
test1: test
},
refetchQueries: [{ query: getStudents}]
})
const aStudent = e.target.value;
setStudent(aStudent);
}}>
<div className="field" onChange={(e) => setName(e.target.value)}>
<label>Student Name:</label>
<input type="text"
value={name}/>
</div>
<div className="field" onChange={(e) => setAge(e.target.value)}>
<label>Age:</label>
<input type="text"
value={age}/>
</div>
<div className="field" onChange={(e) => setTest(e.target.value)}>
<label>Test One:</label>
<input type="text"
value={test}/>
</div>
<button type="submit" >submit</button>
</form>
)
}
export default EditStudent;
Your method named in your props setstudent "check left side of passed props"
<EditStudent student={selectedStudent} setstudent={setStudent}/>
and please access it like the following
const EditStudent = ( {setstudent} ) => {}
// or
const EditStudent = ( props ) => {
props.setstudent()
}
And these lines of code don't seem correct, from where you get this selectedStudent? your props named setStudent then you are accessing it to get the method setStudent
const EditStudent = ( setStudent ) => {
const { setStudent } = selectedStudent;
im trying to show the p element when the input filed has value "the user writes something in input field" and hides when the input is empty
import React, {useState} from 'react'
function textInput() {
const [isOpen, setIsOpen] = useState(false)
return (
<>
<input type="text" onKeyUp={() => setIsOpen(!isOpen)} />
{
isOpen ?
<p>result</p>
: null
}
</>
)
}
export default textInput
Rather than using an isOpen prop, consider maintaining the text in state. Then, if the text is not empty, show the <p> component:
import React, { useState } from "react";
function textInput() {
const [text, setText] = useState("");
return (
<>
<input
type="text"
value={text}
onChange={(e) => {
setText(e.target.value);
}}
/>
{text && <p>result</p>}
</>
);
}
export default textInput;
Please write code like below. It works.
import React, { useState } from "react";
function textInput() {
const [inputText, setInputText] = useState("")
return (
<>
<input type="text" onChange={ (e) => {
setInputText(e.target.value)
}
}/>
{ (inputText !== "") && <p>result: {inputText}</p> }
</>
)
}
export default textInput;
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;