hi guys i am trying to send the data from NewComponent component to App component in react while i am trying to do this it gives me an error and i tried hrad to solve this problem but i coudn't
here as you see this is the component that i will take the information form it and i will send it to newcomponent component exactly i will send expenseData object's information up (from child to parent ) but when i do this it gives me this error (props.onAddNewExpense is not a function)
this is the expense form component
import React , {useState} from "react";
import "./ExpenseForm.css"
const ExpenseForm = (props) => {
//the setEnteredTitle will be like a function i can do a refreshing with it .
const [enteredTitle,setEnteredTitle]=useState("")
const [enteredAmount,setEnteredAmount]=useState("")
const [enteredDate,setEnteredDate]=useState("")
const TitleChangeHandeler=(eo) => {
setEnteredTitle(eo.target.value)
console.log(setEnteredTitle)
}
const amountChangeHnadeler= (eo) => {
setEnteredAmount(eo.target.value)
}
const DateChangeHnadeler= (eo) => {
setEnteredDate(eo.target.value)
}
const submitHandeler=(eo) => {
eo.preventDefault(); // it make me to prevent the dafoult that make the page refresh
const expenseData={
Title:enteredTitle,
Amount:enteredAmount,
Date:new Date(enteredDate)
}
console.log(expenseData)
props.onSaveExpenseData(expenseData)
//when the user make a submit it will give a empty data for the sates
setEnteredTitle("");
setEnteredAmount("");
setEnteredDate("");
}
return (
<form onSubmit={submitHandeler}>
<div className="new-expense__controls">
<div className="new-expense__control">
<label>Title</label>
<input type="text" className="input" onChange={TitleChangeHandeler} value={enteredTitle} />
</div>
<div className="new-expense__control">
<label> Amount </label>
<input type="number" step="0.01" min="0.01" className="input" onChange={amountChangeHnadeler} value={enteredAmount}/>
</div>
<div className="new-expense__control">
<label>DATE</label>
<input type="date" min="3-12-2021" max="01-01-2025" onChange={DateChangeHnadeler} value={enteredDate} />
</div>
<div className="new-expense__actions">
<button type="submit"> Add expense </button>
</div>
</div>
</form>
);
};
export default ExpenseForm;
and this is the parent component
import React from 'react'
import "./NewComponent.css"
import ExpenseForm from "./ExpenseForm"
const NewComponent = (props) => {
const saveExpenseDataHandeler= (enteredExpenseData) => {
const expenseData = {
...enteredExpenseData ,
id:Math.random().toString()
}
props.onAddNewExpense(expenseData)
}
//here we just point to the function we did not execute it here
return (
<div className="new-expense">
<ExpenseForm onSaveExpenseData={saveExpenseDataHandeler} />
</div>
)
}
export default NewComponent
we can see that i fetch the component props and i am trying to take the child data from there
thanks alot for any answer will come
Related
I am trying to learn the basics of React and thought that making a todo list app would be a great first project to help me learn.
I have a basic form to add todos, but, when enter is clicked, the DOM does not change. Here is my app.js code, which I think is where my error might be:
import AddTodoForm from './components/AddTodoForm.js';
import TodoList from './components/TodoList.js';
import { dataList } from './components/AddTodoForm.js';
import { useState } from 'react';
function App() {
const[list, setList] = useState([]);
function update(){
setList(dataList);
console.log(list);
console.log("update function has run.")
}
return (
<div>
<AddTodoForm update = {update} />
<h1>My Todos</h1>
<TodoList todos={list} />
</div>
);
}
export default App;
Here is the code for TodoList.js as somebody had asked for it:
import Todo from './Todo';
function TodoList(props) {
return (
<ul>
{props.todos.map((todo) => (
<Todo
key = {todo.id}
id= {todo.id}
text= {todo.text}
/>
))}
</ul>
)
}
export default TodoList;
here is the AddTodoForm.js:
import { useRef, useState } from 'react';
var idCounter = 1;
export const dataList = [];
function AddTodoForm(props){
const titleInputRef = useRef();
function submitHandler(event){
event.preventDefault();
const enteredTitle= titleInputRef.current.value;
const todoData = {
text: enteredTitle,
id: idCounter,
}
idCounter++;
console.log(todoData);
dataList.push(todoData);
}
return (
<div className="card">
<h2>Add New Todos</h2>
<form onSubmit={(event) => {submitHandler(event); }}>
<div>
<label htmlFor="text">New Todo: </label>
<input type="text" required id="text" ref={titleInputRef}></input>
</div> <br />
<div>
<button className="btn" onClick = {props.update}>Add Todo</button>
</div>
</form>
</div>
)
}
export default AddTodoForm;
I have checked the log and the update function runs. Also, if I make a slight change to my code, the todos I had entered will appear on the screen but I am not sure why the DOM does not change when the update function runs.
This is my first post on here so I wasn't sure how much of my code to include. Please ask if you need the code from my other components.
Many thanks in advance :)
Calling dataList.push(todoData) won't change dataList itself, only its content, and React doesn't check the content to update the DOM. You could use the Spread syntax to have a completely new dataList.
You could even get rid of that dataList, and use the empty array given to useState. Update your update function slightly, and it should work:
import AddTodoForm from "./components/AddTodoForm.js";
import TodoList from "./components/TodoList.js";
import { useState } from "react";
function App() {
const [list, setList] = useState([]);
function update(text) {
// this is for learning; consider using a proper id in real world
setList([...list, { text: text, id: list.length + 1 }]);
}
return (
<div>
<AddTodoForm update={update} />
<h1>My Todos</h1>
<TodoList todos={list} />
</div>
);
}
export default App;
import { useRef } from "react";
function AddTodoForm(props) {
const titleInputRef = useRef();
function submitHandler(event) {
event.preventDefault();
props.update(titleInputRef.current.value);
}
return (
<div className="card">
<h2>Add New Todos</h2>
<form onSubmit={submitHandler}>
<div>
<label htmlFor="text">New Todo: </label>
<input type="text" required id="text" ref={titleInputRef}></input>
</div>
<br />
<div>
<button className="btn">Add Todo</button>
</div>
</form>
</div>
);
}
export default AddTodoForm;
In App.js, I have removed the update function and instead sent the list and setList as props to the AddTodoForm component.
import AddTodoForm from './components/AddTodoForm.js';
import TodoList from './components/TodoList.js';
import { useState } from 'react';
function App() {
const[list, setList] = useState([]);
return (
<div>
<AddTodoForm setList = {setList} list = {list}/>
<h1>My Todos</h1>
<TodoList todos={list} />
</div>
);
}
export default App;
In ./components/AddTodoForm.js add this peice of code inside the AddTodoForm function.
const update = ()=>{
props.setList(datalist);
console.log(props.list);
console.log("The update function has run.")
}
I hope this might help.
I am going to post my opinion to help you with my knowledge about React. In your above code, you cannot render updated state(list) to reflect TodoList component.
In hook, useEffect insteads 2 component lifecycles of class component idea. I mean, you should reflect useEffect with updated states(list).
useEffect is similar to componentDidMount and componentDidUpdate.
Like this:
enter code here
useEfect(()=>{
setList(dataList);
console.log(list);
console.log("update function has run.")
},[list])
I implemented a Modal which uses a custom Hook in my App that should function as a login mask for a WiFi network. So far the Modal is working and I also already managed to pass the name of the selected SSID to it. However I'm struggling with getting back a variable into my other component from where I launched the modal.
Modal.js:
import React from 'react';
import { createPortal } from 'react-dom';
const Modal = ({ isShowing, connect, ssid, hide }) => isShowing ? createPortal(
<React.Fragment>
<div className="modal-overlay" />
<div className="modal-wrapper" aria-modal aria-hidden tabIndex={-1} role="dialog">
<div className="modal">
<div className="modal-header">
<button type="button" className="modal-close-button" data-dismiss="modal" aria-label="Close" onClick={hide}>
<span aria-hidden="true">×</span>
</button>
</div>
<div className="modal-body">
<p>Connect to: {ssid}</p>
<label>
<form onSubmit={connect}>
<input
id="password"
name="Password"
type="password"
value={"password"}
/>
<button type="submit">Connect</button>
</form>
</label>
</div>
</div>
</div>
</React.Fragment>, document.getElementById("modal")
) : null;
export default Modal;
useModal.js:
import { useState } from 'react';
const useModal = () => {
const [isShowing, setIsShowing] = useState(false);
const [password, setPassword] = useState("password");
function toggle() {
setPassword("HOW");
setIsShowing(!isShowing);
}
return {
password,
isShowing,
}
};
export default useModal;
Settings.js:
import React from "react";
import { useState, useEffect } from "react";
import Modal from "../../components/Modal";
import useModal from "../../components/useModal";
const electron = window.require('electron');
const { ipcRenderer } = electron;
const Settings = ({ reqReload }) => {
const [wifiList, setWifiList] = useState([{ id: "", ssid: 'No Networks available' }]);
const [ssidSelected, setSsidSelected] = useState("127.0.0.1");
const { isShowing, toggle } = useModal();
const updateWifi = (event, args) => {
setWifiList(args);
};
function openDialogue(ssid) {
setSsidSelected(ssid);
toggle();
};
/*
function connectWifi(password) {
console.log("Aktuelles Passwort: ", password);
toggle();
}
*/
useEffect(() => {
ipcRenderer.send('updateWifi');
ipcRenderer.on('wifi_list', updateWifi);
return function cleanup() {
ipcRenderer.removeListener('wifi_list', updateWifi);
};
}, []);
return (
<div className="settings">
<Modal
isShowing={isShowing}
connect={connectWifi}
ssid={ssidSelected}
hide={toggle}
/>
<div className="settings__header">
<h2></h2>
</div>
<div className="settings__body">
<div className="settings__connections">
<div className="settings__connections__wifi">
<p><i>Available Wifi-Networks:</i></p>
<div className="settings__connections__wifi__list">
{wifiList.map((item, i) => (
<div className="settings__connections__wifi__list__item" key={i}>
<button className="selectNetworkButton" type="button" onClick={() => openDialogue(item.ssid)}>{item.ssid}</button>
</div>
))}
</div>
</div>
</div>
</div>
</div>
)
};
export default Settings;
The code above already contains some things I tried. As stated before, passing the SSID to the modal worked, but I dont have a clue how to get the password back to Settings.js to handle the data there.
I'd be happy if someone can point me in the right direction!
in order to get back a variable from a child component, you can have the variable you would like to get back as a state in your parent component:
const [yourVariable, setYourVariable] = useState('')
then pass setYourVariable as a props to your modal.
this way you can set youVariable from inside the modal component, and get back its value this way :
// inside modal component
setYoutVariable(...)
I stumbled over another article just now and it seems like using a stateless component as my model leads to a dead end. Will have to convert it to a stateful component in order to extract the data from the form.
I am a newbie in ReactJS and I am making a simple contact manager.
In my InputContact component I take name and email of contact and after submission I store in state variables and pass to parent.
To check my state var is updated , i check my console.
The problem is that, after I submit the form after giving data, I only see a blank line in console. After again clicking on submit, then I see my input in console.
My question is
Why I have to click submit twice , in order to see my state variable getting updated ??
My InputContact.js file
import React from 'react'
import { useState } from 'react';
const InputContact = (props)=>{
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const validateInput = (e)=>{
e.preventDefault();
setName(e.target.fname.value);
setEmail(e.target.femail.value);
console.log(name)
props.addContact(name,email);
}
return(
<>
<form onSubmit={validateInput}>
<label>Name
<input type="text" name='fname' ></input>
</label>
<br/>
<label>Email
<input type="text" name='femail' ></input>
</label>
<button type="submit">Save</button>
</form>
</>
)
};
export default InputContact;
My App.js file is
import Header from './components/Header/Header'
import InputContact from './components/InputContact/InputContact';
import { v4 as uuidv4 } from 'uuid';
function App(){
const [contacts, setContacts] = useState([]);
const addContactFn= (name,email)=>{
setContacts([...contacts, {id:uuidv4(), name:name, email:email}]);
}
return(
<>
<Header />
<InputContact addContact = {addContactFn}/>
</>
)
}
export default App; ```
Your setName call is asynchronous. You cannot guarantee that
console.log(name)
right after
setName(e.target.fname.value);
How you are using state isn't really the normal way. You want to use onChange handlers on the inputs to set the state for each name and email, e.g.
onChange={(e)=> setEmail(e.target.value)}
Then onSubmit of your form should refer to the state variables for name and email, not e.target.value
const validateInput = (e)=>{
e.preventDefault();
props.addContact(name,email);
}
To check the updated value, you can use useEffect hook as
import React from 'react'
import { useState, useEffect } from 'react';
.
.
.
useEffect(() => {
console.log('Name', name);
}, [name]);
const validateInput = (e)=>{
e.preventDefault();
setName(e.target.fname.value);
setEmail(e.target.femail.value);
props.addContact(name,email);
}
.
.
.
I do it like this, so the state updates on typing and when you send it, is already updated.
<form onSubmit={validateInput}>
<label>Name
<input type="text" name='fname' onChange={e => setName(e.target.value) ></input>
</label>
<br/>
<label>Email
<input type="text" name='femail' onChange={e => setEmail(e.target.value) ></input>
</label>
<button type="submit">Save</button>
</form>
Suppose I have a component like this -
const MyForm = ({ formId }) => (
<div>
<input type="text" placeholder="Full name"></input>
<input type="text" placeholder="Email"></input>
</div>
)
export default MyForm;
And then I have my App.js like so -
import React from "react";
import MyForm from "./MyForm";
const App = () => (
<div id="app">
<MyForm formId="formOne"></MyForm>
<MyForm formId="formTwo"></MyForm>
<button onClick={
() => {
// Here, when the user clicks the button,
// I want to get values of both the textboxes,
// from both the component instances
}
}>Submit</button>
</div>
)
export default App;
So basically, what I want is - when the button is clicked, I want to be able to retrieve the values of the textboxes. One way to do this is to raise an event from inside MyForm.js so that every text change is bubbled up to the parent via a callback function prop, but that feels too cumbersome, especially if the form has a lot of fields. Is there any simple or direct way to do this? Do I need to involve global state management tools like Redux?
State inside a component is specific only to that component, the parent , children or sibling of a component have no idea of the state. The only way to communicate the value from one component to another component is via props . In your case, what we need is a state to reside at the App which can then be passed as a prop to both the MyForm Components.
App.js
const [ formState, setFormState ] = useState({ formOne: {fullName: '', Email: ''}, formTwo: '' })
const updateFormValues = (formId, key, value) => {
const stateCopy = JSON.parse(JSON.stringify(formState));
const formToUpdate = stateCopy[formId];
formToUpdate[key] = value;
setFormState(stateCopy)
}
<MyForm formId="formOne" values={formState.formOne} updateFormValues={updateFormValues}></MyForm>
<MyForm formId="formTwo" values={formState.formTwo} updateFormValues={updateFormValues}></MyForm>
MyForm.js
const MyForm = ({ formId, values, updateFormValues }) => {
const onInputChange = (e, key) => {
updateFormValues(formId, key, e.target.value)
}
return(
<div>
<input type="text" onChange={(e) => onInputChange(e, 'fullName'} value={values.fullName} placeholder="Full name"></input>
<input type="text" onChange={(e) => onInputChange(e, 'email'} value={values.email} placeholder="Email"></input>
</div>
)}
export default MyForm;
To have access to data inside children components you need to lift the state to the parent component.
One-way data flow
Identify every component that renders something based on that state.
Find a common owner component (a single component above all the components that need the state in the hierarchy).
Either the common owner or another component higher up in the hierarchy should own the state.
If you can’t find a component where it makes sense to own the state, create a new component solely for holding the state and add it somewhere in the hierarchy above the common owner component.
One way to do this:
import React, { useState } from "react";
function MyForm(props) {
const { handleChange, values } = props;
return (
<div>
<label htmlFor="name">Your name</label>
<input
type="text"
placeholder="Full name"
onChange={handleChange}
value={values.name}
id="name"
name="name"
/>
<label htmlFor="email">Your email</label>
<input
type="email"
placeholder="Email"
onChange={handleChange}
value={values.email}
id="email"
name="email"
/>
</div>
);
}
function App() {
const [values, setValues] = useState({ name: "", email: "" });
const handleChange = (event) => {
const updatedForm = { ...values, [event.target.name]: event.target.value };
setValues(updatedForm);
};
return (
<div id="app">
<MyForm
formId="formOne"
values={values}
handleChange={handleChange}
></MyForm>
<button
onClick={() => {
console.log(values);
}}
>
Submit
</button>
</div>
);
}
export default App;
I have a react component which manage user logging in and out, when user type email and password in the login field the whole component (Navbar) re-render to Dom in every keystroke unnecessarily thus reduces speed.
How can I prevent Navbar from re-rendering when user type their credential in login fild ?
import React, { useContext,useState } from 'react';
import { Postcontext } from '../contexts/Postcontext';
import axios from 'axios';
const Navbar = () => {
const { token,setToken } = useContext(Postcontext);
const [email,setEmail] = useState(''); **state manages user email for login**
const [password,setPassword] = useState(''); **state manages user password for login**
const[log,setLog] = useState(true) **state manages if user logged in or not based on axios post request**
const login=(e)=>{
//function for login using axios
})
}
const logout=(e)=>{
//function for logout using axios
}
return (
<div className="navbar">
{log?(
<form>
<input value={email} type="text" placeholder="email" onChange={(e)=>setEmail(e.target.value)}/>
<input value={password} type="text" placeholder="password" onChange={(e)=>setPassword(e.target.value)}/>
<button onClick={login}>login</button>
</form>
):(
<button onClick={logout}>logout</button>
)}
</div>
);
}
export default Navbar;
It is because it is same component which needs re-render to reflect input text changes. If you want your email to change but not effect Navbar then create a child component and move inputs into that component, manage input values using useState() there in child component and when you finally submit and user is logged in then you can either update some global state like redux store or global auth context to reflect and rerender Navbar.
So, I had the same issue and I was able to solve it using useRef and useCallback and I will try to explain in Q&A form. Sorry if I am not that clear, this is my first StackOverFlow comment and I am a beginner in React :)
Why useRef?
React re-renders every time it sees a component has updated by checking if previous and current object are same or not. In case of useRef it checks the object Id only and not the content inside it i.e. value of current inside the Ref component. So if you change the value of current React will not consider that. (and that's what we want)
Why useCallback?
Simply because it will run only when we call it or one (or more) of the dependencies have changed. As we are using Ref so it won't be called when the current value inside it has changed.
More info: https://reactjs.org/docs/hooks-reference.html
Based on above info your code should look like this (only doing login part):
import React, { useContext, useRef } from 'react';
const App = () => {
const emailRef = useRef(null);
const passwordRef = useRef(null);
const logRef = useRef(null);
const loginUpdate = useCallback( async (event) => {
event.preventDefault();
// Your logic/code
// For value do:
// const email = emailRef.current.value;
}, [emailRef, passwordRef, logRef]);
return (
<div className="navbar">
{log?(
<form>
<input
ref={emailRef}
type="text"
placeholder="email"
/>
<input
ref={passwordRef}
type="text"
placeholder="password"
/>
<button onClick={loginUpdate}>login</button>
</form>
):(
// Not doing this part because I am lazy :)
<button onClick={logout}>logout</button>
)}
</div>
);
}
export default App;
Had a few typos. It works for me
https://codesandbox.io/s/cold-sun-s1225?file=/src/App.js:163-208
import React, { useContext,useState } from 'react';
// import { Postcontext } from '../contexts/Postcontext';
// import axios from 'axios';
const App = () => {
// const { token,setToken } = useContext();
const [email,setEmail] = useState('');
const [password,setPassword] = useState('');
const[log,setLog] = useState(true)
const login=(e)=>{
//function for login using axios
}
const logout=(e)=>{
//function for logout using axios
}
return (
<div className="navbar">
{log?(
<form>
<input value={email} type="text" placeholder="email" onChange={(e)=>setEmail(e.target.value)}/>
<input value={password} type="text" placeholder="password" onChange={(e)=>setPassword(e.target.value)}/>
<button onClick={login}>login</button>
</form>
):(
<button onClick={logout}>logout</button>
)}
</div>
);
}
export default App;