I'm am new to React and this is my first project.
I am trying to pass the id (which comes from item.id) of a transaction located in my Summary functional component to my EditTransaction functional component.
It is giving me this error: TypeError: Cannot read property 'props' of undefined
I've tried:
But it does not seem to work either...
App.js
import './App.css';
import {BrowserRouter as Router, Switch, Route, Link, Redirect} from 'react-router-dom';
import Home from "./views/Home";
import Summary from './views/Summary';
import Profile from './views/Profile';
import Groups from './views/Groups';
import About from './views/About';
import Transaction from './views/Transaction';
import Signin from "./components/Signin";
import EditTransaction from "./views/EditTransaction";
import 'bootstrap/dist/css/bootstrap.min.css';
function App() {
return (
<div className="App">
<Router>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/Signin" component={Signin} />
<Route exact path="/About" component={About} />
<Route exact path="/Profile" component={Profile} />
<Route exact path="/Groups" component={Groups} />
<Route exact path="/Summary" component={Summary} />
<Route exact path="/Transaction" component={Transaction} />
<Route exact path="/EditTransaction/:id" component={EditTransaction} />
</Switch>
</Router>
</div>
);
}
export default App;
Summary.js
import React, { useEffect, useState } from 'react';
import {NavLink} from 'react-router-dom';
import Button from '#material-ui/core/Button';
import { Redirect } from 'react-router-dom';
import EditTransaction from '../views/EditTransaction';
import SigninNavBar from '../components/SigninNavBar';
function Summary(){
const [error, setError] = useState(null);
const [isLoaded, setIsLoaded] = useState(false);
const [trans, setTrans] = useState([]);
const [signin, setSignin] = useState({signin:localStorage.getItem('token') ? true : false});
const [details, setDetails] = useState({username:"", email:"", password:""});
console.log("Token: " + localStorage.getItem('token'));
const options = {
method: 'GET',
headers: {
'Content-type': 'application/json; charset=UTF-8',
'Accept': 'application/json',
'Authorization': `JWT ${localStorage.getItem('token')}`
}
};
useEffect(() => {
fetch("http://34.94.76.5/api/transactions/get/", options)
.then(response => {
if (response.status !== 200) {
console.log(response.status);
setError(response);
}
response.json().then(data => {
setIsLoaded(true);
setTrans(data);
});
});
}, []);
if (error) {
return (<Redirect to="/Signin" />);
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<div>
<SigninNavBar />
<table className="table">
<thead>
<th>ID</th>
<th>Member ID</th>
<th>Created</th>
<th>Category</th>
<th>Description</th>
<th>Amount</th>
</thead>
{trans.map(item => (
<tbody>
<tr>
<td> {item.id} </td>
<td> {item.member_id} </td>
<td> {item.created} </td>
<td> {item.category} </td>
<td> {item.description} </td>
<td> {item.amount} </td>
<td>
<Button variant="contained" color="primary">
<NavLink className="text-white" to={{ pathname:`/EditTransaction/${item.id}`, state:{id: item.id} }}>
Edit
</NavLink>
</Button>
</td>
</tr>
</tbody>
))}
</table>
</div>
);
}
}
export default Summary;
EditTransaction.js
import React, { useEffect, useState } from 'react';
import { Redirect } from 'react-router-dom';
import moment from 'moment';
import SigninNavBar from '../components/SigninNavBar.js';
import Summary from '../views/Summary.js';
var currentDate = moment().format("MM/DD/YYYY HH:mm:ss");
function EditTransaction(props) {
const [error, setError] = useState(null);
const [trans, setTrans] = useState([]);
const [tranUpdated, settranUpdated] = useState({id:0, member_id:'', category:'', description:'', amount:0});
const [isLoaded, setIsLoaded] = useState(false);
const [tran, setTran] = useState([]);
//GET request for specific id
const optionsGET = {
method: 'GET',
headers: {
'Content-type': 'application/json; charset=UTF-8',
'Accept': 'application/json',
'Authorization': `JWT ${localStorage.getItem('token')}`
}
};
useEffect(() => {
fetch("http://34.94.76.5/api/transactions/get/" + this.props.location.state + "/", optionsGET)
.then(response => {
if (response.status !== 200) {
console.log(response.status);
setError(response);
}
response.json().then(data => {
setIsLoaded(true);
setTrans(data);
});
});
}, []);
//PUT request to API for transaction
const optionsPUT = {
method: 'PUT',
headers: {
'Content-type': 'application/json; charset=UTF-8',
'Accept': 'application/json',
'Authorization': `JWT ${localStorage.getItem('token')}`
},
body:JSON.stringify(tranUpdated)
}
const updateTransaction = e => {
e.preventDefault();
fetch("http://34.94.76.5/api/transactions/get/" + this.props.location.state + "/", optionsPUT)
.then((response) => console.log('reponse: ' + response.json()))
.then((message) => console.log('message: ' + message))
}
if (error) {
return (<Redirect to="/Signin" />);
} else{
return (
<div className="wrapper">
<SigninNavBar />
<form>
<h1>Update Transaction</h1>
{trans.map(item => (
<div className="form-horizantal">
<fieldset>
<div className="form-group row">
<label className="col-md-12"><p>{currentDate}</p></label>
</div>
<div className="form-group row">
<label className="col-md-12">
<p>Member ID {item.member_id}</p>
<input type="text" name="member_id" placeholder={item.member_id} onChange={e => settranUpdated({ ...tranUpdated, member_id: e.target.value })} />
</label>
</div>
<div className="form-group row">
<label className="col-md-12">
<p>Transaction ID</p>
<input type="text" name="id" placeholder={item.id} onChange={e => settranUpdated({ ...tranUpdated, id: e.target.value })} />
</label>
</div>
<div className="form-group row">
<label className="col-md-12">
<p>Category</p>
<input type="text" name="category" placeholder={item.category} onChange={e => settranUpdated({ ...tranUpdated, category: e.target.value })} />
</label>
</div>
<div className="form-group row">
<label className="col-md-12">
<p>Description</p>
<input type="text" name="description" placeholder={item.description} onChange={e => settranUpdated({ ...tranUpdated, description: e.target.value })} />
</label>
</div>
<div className="form-group row">
<label className="col-md-12">
<p>Amount</p>
<input type="text" name="amount" placeholder={item.amount} onChange={e => settranUpdated({ ...tranUpdated, amount: e.target.value })} />
</label>
</div>
</fieldset>
<button type="submit" onClick={updateTransaction}>Update</button>
</div>
))}
</form>
</div>
);
}
}
export default EditTransaction;
You shouldn't use the this keyword in a function component
Consider changing this.props.location.state to just props.location.state.id
Related
I have an EditProfile functional component that initially loads perfect, but loses state whenever the user refreshes their page with the form open. Instead of pre-filling the inputs with the 'user' state/values, it now sets the inputs as blank, and the 'user' object from const [user, setUser] = useState({}) console.logs as an empty object.
Here is the EditProfile component:
import React, {useEffect, useState} from 'react'
import './EditProfile.css'
export default function EditProfile(props) {
const [user, setUser] = useState({})
let handleChange = (e) => {
setUser({...user, [e.target.name]: e.target.value})
}
let handleSubmit = async () => {
let body = {
_id: user._id,
email: user.email,
username: user.username,
shortDescription: user.shortDescription,
fullDescription: user.fullDescription,
paymentInfo: user.paymentInfo,
publisherAgreement: user.publisherAgreement
}
let options = {
method: 'PUT',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(body)
}
await fetch('/api/users/editProfile', options)
.then(res => res.json())
.then(data => {
this.props.setUserInState(data)
})
}
useEffect(() => {
(async() => {
setUser(props.user)
})()
},[])
console.log('here is the user: ' + JSON.stringify(user))
return (
<form className='editProfileForm' onSubmit={handleSubmit}>
<input type='hidden' value={user._id}></input>
<input type='hidden' value={user.email}></input>
<div>
<h4>Edit Profile</h4>
<div className="form-group address-update">
<label className="inputUD"><span className="label"><b>Username</b></span></label>
<input type="text" className="form-control" name="username" onChange={handleChange} value={user.username}></input>
</div>
<div className="form-group address-update">
<label className="inputUD"><span className="label"><b>Email</b></span></label>
<input type="text" className="form-control" name="email" onChange={handleChange} value={user.email}></input>
</div>
{user.publisherAgreement &&
<div>
<div className="form-group">
<label className="inputUD"><span className="label"><b>Short Description</b></span></label>
<textarea type="text" className="form-control" name="shortDescription" onChange={handleChange} value={user.shortDescription}></textarea>
</div>
<div className="form-group">
<label className="inputUD"><span className="label"><b>Full Description</b></span></label>
<textarea type="text" className="form-control" name="fullDescription" onChange={handleChange} value={user.fullDescription}></textarea>
</div>
<div className="form-group address-update">
<label className="inputUD"><span className="label"><b>Fake Payment Info</b></span></label>
<input type="text" className="form-control" name="paymentInfo" onChange={handleChange} value={user.paymentInfo}></input>
</div>
</div>
}
<button type="submit" className="btn btn-success">Submit</button>
</div>
</form>
)
}
And here is the App.jsx component where it is called on (I cut out imports to save space):
state = {
user: false,
}
setUserInState = (incomingUserData) => {
console.log('incoming userdata: ' + JSON.stringify(incomingUserData))
this.setState({user: incomingUserData})
}
componentDidMount() {
let token = localStorage.getItem('token')
if (token) {
const payload = JSON.parse(atob(token.split('.')[1]))
if (payload.exp < Date.now() / 1000) {
localStorage.removeItem('token')
token = null
} else {
this.setState({ user: payload.user })
}
}
}
render() {
return (
<div className="App">
<Navbar user={this.state.user} setUserInState={this.setUserInState}/>
<Routes>
<Route path='/ideas/create' element={<NewIdea user={this.state.user} setUserInState={this.setUserInState}/>}/>
<Route path='/users/showProfile' element={<UserProfile user={this.state.user} setUserInState={this.setUserInState}/>}/>
<Route path='/users/editProfile' element={<EditProfile user={this.state.user} setUserInState={this.setUserInState}/>}/>
<Route path='/publishers/becomePublisher' element={<BecomePublisher user={this.state.user} setUserInState={this.setUserInState}/>} />
<Route path='/users/addSubscription'/>
<Route path='/users/removeSubscription'/>
<Route path='/publishers/show/:id' element={<PubProfile user={this.state.user}/>}/>
<Route path='/ideas/show/:id' element={<PubIdeas user={this.state.user}/>}/>
<Route path='/ideas/ideasFeed/:userId' element={<IdeasFeed user={this.state.user}/>}/>
<Route path='/discover/:id' element={<Discover user={this.state.user} setUserInState={this.setUserInState}/>}/>
<Route path='*' element={<Navigate to='/discover/:id' replace />}/>
</Routes>
</div>
)
}
}
The page is accessed via a "Link to" in the Navbar component.
While the form works fine as long as the user doesn't hit refresh, I need the form to be able to handle a refresh and still work.
Any help is much appreciated.
I have an EditProfile functional component that initially loads perfect, but loses state whenever the user refreshes their page with the form open. Instead of pre-filling the inputs with the 'user' state/values, it now sets the inputs as blank, and the 'user' object from const [user, setUser] = useState({}) console.logs as an empty object.
Here is the EditProfile component:
import React, {useEffect, useState} from 'react'
import './EditProfile.css'
export default function EditProfile(props) {
const [user, setUser] = useState({})
let handleChange = (e) => {
setUser({...user, [e.target.name]: e.target.value})
}
let handleSubmit = async () => {
let body = {
_id: user._id,
email: user.email,
username: user.username,
shortDescription: user.shortDescription,
fullDescription: user.fullDescription,
paymentInfo: user.paymentInfo,
publisherAgreement: user.publisherAgreement
}
let options = {
method: 'PUT',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(body)
}
await fetch('/api/users/editProfile', options)
.then(res => res.json())
.then(data => {
this.props.setUserInState(data)
})
}
useEffect(() => {
(async() => {
setUser(props.user)
})()
},[])
console.log('here is the user: ' + JSON.stringify(user))
return (
<form className='editProfileForm' onSubmit={handleSubmit}>
<input type='hidden' value={user._id}></input>
<input type='hidden' value={user.email}></input>
<div>
<h4>Edit Profile</h4>
<div className="form-group address-update">
<label className="inputUD"><span className="label"><b>Username</b></span></label>
<input type="text" className="form-control" name="username" onChange={handleChange} value={user.username}></input>
</div>
<div className="form-group address-update">
<label className="inputUD"><span className="label"><b>Email</b></span></label>
<input type="text" className="form-control" name="email" onChange={handleChange} value={user.email}></input>
</div>
{user.publisherAgreement &&
<div>
<div className="form-group">
<label className="inputUD"><span className="label"><b>Short Description</b></span></label>
<textarea type="text" className="form-control" name="shortDescription" onChange={handleChange} value={user.shortDescription}></textarea>
</div>
<div className="form-group">
<label className="inputUD"><span className="label"><b>Full Description</b></span></label>
<textarea type="text" className="form-control" name="fullDescription" onChange={handleChange} value={user.fullDescription}></textarea>
</div>
<div className="form-group address-update">
<label className="inputUD"><span className="label"><b>Fake Payment Info</b></span></label>
<input type="text" className="form-control" name="paymentInfo" onChange={handleChange} value={user.paymentInfo}></input>
</div>
</div>
}
<button type="submit" className="btn btn-success">Submit</button>
</div>
</form>
)
}
And here is the App.jsx component where it is called on (I cut out imports to save space):
state = {
user: false,
}
setUserInState = (incomingUserData) => {
console.log('incoming userdata: ' + JSON.stringify(incomingUserData))
this.setState({user: incomingUserData})
}
componentDidMount() {
let token = localStorage.getItem('token')
if (token) {
const payload = JSON.parse(atob(token.split('.')[1]))
if (payload.exp < Date.now() / 1000) {
localStorage.removeItem('token')
token = null
} else {
this.setState({ user: payload.user })
}
}
}
render() {
return (
<div className="App">
<Navbar user={this.state.user} setUserInState={this.setUserInState}/>
<Routes>
<Route path='/ideas/create' element={<NewIdea user={this.state.user} setUserInState={this.setUserInState}/>}/>
<Route path='/users/showProfile' element={<UserProfile user={this.state.user} setUserInState={this.setUserInState}/>}/>
<Route path='/users/editProfile' element={<EditProfile user={this.state.user} setUserInState={this.setUserInState}/>}/>
<Route path='/publishers/becomePublisher' element={<BecomePublisher user={this.state.user} setUserInState={this.setUserInState}/>} />
<Route path='/users/addSubscription'/>
<Route path='/users/removeSubscription'/>
<Route path='/publishers/show/:id' element={<PubProfile user={this.state.user}/>}/>
<Route path='/ideas/show/:id' element={<PubIdeas user={this.state.user}/>}/>
<Route path='/ideas/ideasFeed/:userId' element={<IdeasFeed user={this.state.user}/>}/>
<Route path='/discover/:id' element={<Discover user={this.state.user} setUserInState={this.setUserInState}/>}/>
<Route path='*' element={<Navigate to='/discover/:id' replace />}/>
</Routes>
</div>
)
}
}
The page is accessed via a "Link to" in the Navbar component.
While the form works fine as long as the user doesn't hit refresh, I need the form to be able to handle a refresh and still work.
EDIT/SOLUTION:
I simply passed props.user into the useEffect dependencies array like so:
useEffect(() => {
(async() => {
setUser(props.user)
})()
},[props.user])
The page now sets props into state after a page refresh, just like it does on the first render.
Also, inside my fetch call I switched this.props.setUserInState(data) to just setUser(data).
So ive got this error, im guessing its something with my use navigate, but im not shure.
Ive have read alot and searched for the solution but i cant find any way to solve the issue.
The issue only comes after ive have logged in. At the moment i dont have any access token on the backend.
react_devtools_backend.js:4026 Warning: Cannot update a component (BrowserRouter) while rendering a different component (Login). To locate the bad setState() call inside Login, follow the stack trace as described in https://reactjs.org/link/setstate-in-render
at Login (http://localhost:3000/static/js/bundle.js:3571:82)
at div
at LoginPage
at Routes (http://localhost:3000/static/js/bundle.js:129199:5)
at Router (http://localhost:3000/static/js/bundle.js:129132:15)
at BrowserRouter (http://localhost:3000/static/js/bundle.js:127941:5)
import React from 'react';
import { useRef, useState, useEffect } from 'react'
// import { useContext } from 'react'
//import AuthContext from './context/AuthProvider';
import axios from './api/axios';
const LOGIN_URL = '/login';
const REG_URL = '/newUser'
export default function Login() {
const [authMode, setAuthMode] = useState("signin");
const changeAuthMode = () => {
setAuthMode(authMode === "signin" ? "signup" : "signin");
}
//const { setAuth } = useContext(AuthContext);
const userRef = useRef();
const errRef = useRef();
const [userName, setUserName] = useState("");
const [password, setPassword] = useState("");
const [errMsg, setErrMsg] = useState('');
const [success, setSuccess] = useState(false);
const [register, setRegister] = useState(false);
useEffect(() => {
userRef.current.focus();
}, [])
useEffect(() => {
setErrMsg('');
}, [userName, password])
const handleRegister = (e) => {
// prevent the form from refreshing the whole page
e.preventDefault();
// set configurations
const RegConfiguration = {
method: "post",
url: REG_URL,
data: {
userName,
password,
},
};
// make the API call
axios(RegConfiguration)
.then((result) => {
setRegister(true);
})
.catch((error) => {
error = new Error();
});
};
///HANDLE Login part.
const handleLogin = async (e) => {
e.preventDefault();
try {
await axios.post(LOGIN_URL,
JSON.stringify({ userName, password }),
{
headers: { 'Content-type': 'application/json' },
withCredentials: false,
}
);
//const token = response?.data?.token;
//setAuth({ userName, password, token })
//console.log(JSON.stringify(respone));
setUserName('');
setPassword('');
setSuccess(true);
} catch (err) {
if (!err?.response) {
setErrMsg('No Server Response');
} else if (err.response?.status === 400) {
setErrMsg('Missing Username or Password');
} else if (err.response?.status === 401) {
setErrMsg('Unauthorized');
}
else {
setErrMsg('Login Failed');
}
errRef.current.focus();
}
}
const Navigate = useNavigate();
if (authMode === "signin") {
return (
<> {success ? (
Navigate('/authlog')
) : (
<div className="Auth-form-container">
<form className="Auth-form" >
<div className="Auth-form-content">
<h3 className="Auth-form-title">Sign In</h3>
<div className="text-center">
Not registered yet?{" "}
<span className="link-primary" onClick={changeAuthMode} >
Sign Up
</span>
</div>
<div className="form-group mt-3">
<label>Username </label>
<input
className="form-control mt-1"
id='userName'
ref={userRef}
name="userName"
value={userName}
placeholder="username"
onChange={(e) => setUserName(e.target.value)}
/>
</div>
<div className="form-group mt-3">
<label>Password</label>
<input
className="form-control mt-1"
name="password"
type="password"
id='password'
value={password}
placeholder="password"
required
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<div className="d-grid gap-2 mt-3">
<button
type="submit"
className="btn btn-primary"
onClick={(e) => handleLogin(e)}
>
Logga in
</button>
<p ref={errRef} className={errMsg ? "errmsg" : "offscreen"} aria-live="assertive">{errMsg}</p>
</div>
</div>
</form>
</div>
)}</>
)
}
// "Register page"
return (
<div className="Auth-form-container">
<form className="Auth-form" onSubmit={(e) => handleRegister(e)}>
<div className="Auth-form-content">
<h3 className="Auth-form-title">Sign In</h3>
<div className="text-center">
Already registered?{" "}
<span className="link-primary" onClick={changeAuthMode}>
Go to Sign In
</span>
</div>
<div className="form-group mt-3">
<label>Register Your Username here</label>
<input
className="form-control mt-1"
name="userName"
value={userName}
placeholder="ure username here:"
onChange={(event) => setUserName(event.target.value)}
/>
</div>
<div className="form-group mt-3">
<label>Register Youre password</label>
<input
className="form-control mt-1"
type="password"
name="password"
value={password}
placeholder="ure password here"
onChange={(event) => setPassword(event.target.value)}
/>
</div>
<div className="d-grid gap-2 mt-3">
<button type="submit" className="btn btn-primary" onClick={handleRegister}>
Registera
</button>
{/* display success message */}
{register ? (
<p className="text-success">You Are Now Registered Successfully</p>
) : (
<p className="text-danger">Please register your account here</p>
)}
</div>
</div>
</form>
</div>
);
}
My App Component is here
import React, { useEffect } from 'react';
import './App.css';
import Navbar from './components/layout/Navbar';
import Landing from './components/layout/Landing';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Login from './components/auth/Login';
import Register from './components/auth/Register';
import { Provider } from 'react-redux';
import store from './store';
import Alert from './components/layout/Alert';
import setAuthToken from './utils/setAuthToken';
import { loadUser } from './actions/auth';
import Dashboard from './components/dashboard/Dashboard';
import PrivateRoute from './components/routing/PrivateRoute';
import CreateProfile from './components/profile-form/CreateProfile';
import EditProfile from './components/profile-form/EditProfile';
import AddExperience from './components/profile-form/AddExperience';
import AddEducation from './components/profile-form/AddEducation';
if (localStorage.token) {
setAuthToken(localStorage.token);
}
const App = () => {
useEffect(() => {
store.dispatch(loadUser());
}, []);
return (
<Provider store={store}>
<Router>
<>
<Navbar />
<Route exact path='/' component={Landing} />
<section className='container'>
<Alert />
<Switch>
<Route exact path='/login' component={Login} />
<Route exact path='/register' component={Register} />
<PrivateRoute exact path='/dashboard' component={Dashboard} />
<PrivateRoute
exact
path='/create-profile'
component={CreateProfile}
/>
<PrivateRoute
exact
path='/edit-profile'
component={EditProfile}
/>
<PrivateRoute
exact
to='/add-experience'
component={AddExperience}
/>
<PrivateRoute
exact
path='/add-education'
component={AddEducation}
/>
</Switch>
</section>
</>
</Router>
</Provider>
);
};
export default App;
My AddExperience Component is here
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { addExperience } from '../../actions/profile';
import { Link, withRouter } from 'react-router-dom';
const AddExperience = ({ addExperience, history }) => {
const [formData, setFormData] = useState({
company: '',
title: '',
location: '',
from: '',
to: '',
current: false,
description: '',
});
const [toDateDisabled, toggleDisabled] = useState(false);
const { company, title, location, from, to, current, description } = formData;
const onChange = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
return (
<>
<h1 className='large text-primary'>Add An Experience</h1>
<p className='lead'>
<i className='fas fa-code-branch'></i> Add any developer/programming
positions that you have had in the past
</p>
<small>* = required field</small>
<form
className='form'
onSubmit={(e) => {
e.preventDefault();
addExperience(formData, history);
}}
>
<div className='form-group'>
<input
type='text'
placeholder='* Job Title'
name='title'
value={title}
onChange={(e) => onChange(e)}
required
/>
</div>
<div className='form-group'>
<input
type='text'
placeholder='* Company'
name='company'
value={company}
onChange={(e) => onChange(e)}
required
/>
</div>
<div className='form-group'>
<input
type='text'
placeholder='Location'
name='location'
value={location}
onChange={(e) => onChange(e)}
/>
</div>
<div className='form-group'>
<h4>From Date</h4>
<input
type='date'
name='from'
value={from}
onChange={(e) => onChange(e)}
/>
</div>
<div className='form-group'>
<p>
<input
type='checkbox'
name='current'
checked={current}
value={current}
onChange={(e) => {
setFormData({ ...formData, current: !current });
toggleDisabled(!toDateDisabled);
}}
/>{' '}
Current Job
</p>
</div>
<div className='form-group'>
<h4>To Date</h4>
<input
type='date'
name='to'
value={to}
onChange={(e) => onChange(e)}
disabled={toDateDisabled ? 'disable' : ''}
/>
</div>
<div className='form-group'>
<textarea
name='description'
cols='30'
rows='5'
placeholder='Job Description'
value={description}
onChange={(e) => onChange(e)}
></textarea>
</div>
<input type='submit' className='btn btn-primary my-1' />
<a className='btn btn-light my-1' href='dashboard.html'>
Go Back
</a>
</form>
</>
);
};
AddExperience.propTypes = {
addExperience: PropTypes.func.isRequired,
};
export default connect(null, { addExperience })(AddExperience);
Here is my AddEducation Component
import React, { useState, Fragment } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { addEducation } from '../../actions/profile';
import { Link, withRouter } from 'react-router-dom';
const AddEducation = ({ addEducation, history }) => {
const [formData, setFormData] = useState({
school: '',
degree: '',
fieldofstudy: '',
from: '',
to: '',
current: false,
description: '',
});
const [toDateDisabled, toggleDisabled] = useState(false);
const {
school,
degree,
fieldofstudy,
from,
to,
current,
description,
} = formData;
const onChange = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
return (
<Fragment>
<h1 className='large text-primary'>Add Your Education</h1>
<p className='lead'>
<i className='fas fa-code-branch'></i> Add any School or bootcamp that
you have attended
</p>
<small>* = required field</small>
<form
className='form'
onSubmit={(e) => {
e.preventDefault();
addEducation(formData, history);
}}
>
<div className='form-group'>
<input
type='text'
placeholder='* School or Bootcamp'
name='school'
value={school}
onChange={(e) => onChange(e)}
required
/>
</div>
<div className='form-group'>
<input
type='text'
placeholder='* Degree or Certificate'
name='degree'
value={degree}
onChange={(e) => onChange(e)}
required
/>
</div>
<div className='form-group'>
<input
type='text'
placeholder='fieldofstudy'
name='fieldofstudy'
value={fieldofstudy}
onChange={(e) => onChange(e)}
/>
</div>
<div className='form-group'>
<h4>From Date</h4>
<input
type='date'
name='from'
value={from}
onChange={(e) => onChange(e)}
/>
</div>
<div className='form-group'>
<p>
<input
type='checkbox'
name='current'
checked={current}
value={current}
onChange={(e) => {
setFormData({ ...formData, current: !current });
toggleDisabled(!toDateDisabled);
}}
/>{' '}
Current Job
</p>
</div>
<div className='form-group'>
<h4>To Date</h4>
<input
type='date'
name='to'
value={to}
onChange={(e) => onChange(e)}
disabled={toDateDisabled ? 'disable' : ''}
/>
</div>
<div className='form-group'>
<textarea
name='description'
cols='30'
rows='5'
placeholder='Programme Description'
value={description}
onChange={(e) => onChange(e)}
></textarea>
</div>
<input type='submit' className='btn btn-primary my-1' />
<a className='btn btn-light my-1' href='dashboard.html'>
Go Back
</a>
</form>
</Fragment>
);
};
AddEducation.propTypes = {
addEducation: PropTypes.func.isRequired,
};
export default connect(null, { addEducation })(AddEducation);
And Lastly here is the DashboardActions Component
import React from 'react';
import { Link } from 'react-router-dom';
const DashboardActions = () => {
return (
<div className='dash-buttons'>
<Link to='/edit-profile' className='btn btn-light'>
<i className='fas fa-user-circle text-primary' /> Edit Profile
</Link>
<Link to='/add-experience' className='btn btn-light'>
<i className='fab fa-black-tie text-primary' /> Add Experience
</Link>
<Link to='/add-education' className='btn btn-light'>
<i className='fas fa-graduation-cap text-primary' /> Add Education
</Link>
</div>
);
};
export default DashboardActions;
The Problem is When i Click on AddExperience it opens correctly with correct url but when i click on AddEducation it Opens the same Add Experience Form but url changed Correctly.
There is a typo here:
<PrivateRoute
exact
to='/add-experience'
component={AddExperience}
/>
The 'to' should be replaced with 'path'
I am new to ReactJS and building one simple application using few components like UserForm, UserGrid and App.
UserForm displays simple textbox and button like below.
and when user input some text inside textbox and hit the save button it will display data in UserGrid component as per the screenshot.
but i can't figure it out how edit will work ? like when i hit the edit it will fetch the information from the grid and fill the textbox so i can update the data, can someone please help me on this ? how can i achieve this ?
below is my code
UserForm.js
import React,{createRef} from 'react';
const UserForm = (props) =>{
const username = createRef();
const saveUser = () =>{
debugger
if(username.current.value !== ''){
props.handleSubmit(username.current.value);
username.current.value='';
username.current.focus();
}
}
return(
<div className="row">
<div className="col-md-6">
<div className="form-group">
<input type="text" className="form-control" placeholder="Username" ref={username} />
</div>
</div>
<div className="col-md-12">
<button className="btn btn-primary" onClick={saveUser}>Save</button>
</div>
</div>
)
}
export default UserForm;
UserGrid.js
import React from 'react';
const UserGrid = (props) =>{
debugger
return(
<div className="row">
<div className="col-md-12">
<table className="table">
<tr>
<th>
Username
</th>
<th>
</th>
</tr>
{
props.list.map(item =>
<tr>
<td>
{item}
</td>
<td>
<button>Edit</button>
<button>Delete</button>
</td>
</tr>
)
}
</table>
</div>
</div>
)
}
export default UserGrid;
App.js
import React,{useState} from 'react';
import UserForm from './UserForm';
import UserGrid from './UserGrid';
function App() {
const [list, setList] = useState([]);
const handleSubmit = (username) =>{
setList([...list,username]);
}
return (
<div className="App">
<UserForm handleSubmit={handleSubmit}></UserForm>
<UserGrid list={list}></UserGrid>
</div>
);
}
export default App;
You have to bring your edit state up to App component rather than using ref in the child component. Just like you have handleSubmit in App.js have a handleEdit function and pass that function to UserGrid.
function App() {
const [list, setList] = useState([]);
const [name, setName] = useState('');
const [editIndex, setEditIndex] = useState(null);
const handleSubmit = () => {
if(editIndex) {
setList([...list.slice(0, index), name, ...list.slice(index+1)])
setEditIndex(null)
} else {
setList([...list, name]);
}
setName('')
}
return (
<div className="App">
<UserForm name={name} setName={setName} handleSubmit={handleSubmit}></UserForm>
<UserGrid setEditIndex={setEditIndex} list={list}></UserGrid>
</div>
);
}
UserForm.js
import React,{createRef} from 'react';
const UserForm = ({name, setName}) =>{
const saveUser = () =>{
debugger
if(name !== ''){
props.handleSubmit();
}
}
return(
<div className="row">
<div className="col-md-6">
<div className="form-group">
<input type="text" className="form-control" placeholder="Username" value={name} onChange={(e) => setName(e.target.value)} />
</div>
</div>
<div className="col-md-12">
<button className="btn btn-primary" onClick={saveUser}>Save</button>
</div>
</div>
)
}
export default UserForm;
To support edit:
{
props.list.map((item, index) =>
<tr>
<td>
{item}
</td>
<td>
<button onClick={setEditIndex(index)}>Edit</button>
<button>Delete</button>
</td>
</tr>
)
}
This is working example while i am using your code.
Nothing special in App.js. You can take a look a logic.
App.js
import React, { useState } from "react";
import UserForm from "./UserForm";
import UserGrid from "./UserGrid";
function App() {
const [list, setList] = useState([]);
const handleSubmit = username => {
setList([...list, username]);
};
const editList = item => {
setList(
list.map(l => ({
...l,
username: l.id === item.id && item.newValue ? item.newValue : l.username
}))
);
};
const deleteList = id => {
setList(list.filter(l => l.id !== id));
};
return (
<div className="App">
<UserForm handleSubmit={handleSubmit}></UserForm>
<UserGrid
list={list}
editList={editList}
deleteList={deleteList}
></UserGrid>
</div>
);
}
export default App;
This is UserForm Component. I think you will have problems, if you dont have someting like ID, cuz if you have lists with same names, your delete and edit function will work wrong.
UseForm.js
import React, { createRef } from "react";
const UserForm = ({ handleSubmit }) => {
const username = createRef();
const saveUser = () => {
if (username.current.value !== "") {
const randomId = () => "_" + Math.random().toString(36).substr(2, 9);
handleSubmit({ username: username.current.value, id: randomId() });
username.current.value = "";
username.current.focus();
}
};
return (
<div className="row">
<div className="col-md-6">
<div className="form-group">
<input
type="text"
className="form-control"
placeholder="Username"
ref={username}
/>
</div>
</div>
<div className="col-md-12">
<button className="btn btn-primary" onClick={saveUser}>
Save
</button>
</div>
</div>
);
};
export default UserForm;
And this is UserGrid component. I hope you will undarstand the code. If you have questions, go ahead.
UseGrid.js
import React, { useState } from "react";
const UserGrid = ({ list, editList, deleteList }) => {
const [isEditable, setIsEditable] = useState(false);
const [value, setValue] = useState("");
return (
<div className="row">
<div className="col-md-12">
<table className="table">
<tr>
<th>Username</th>
<th></th>
</tr>
{list.map(item => (
<tr>
<td>{item.username}</td>
{isEditable ? (
<>
<td>
<input
type="text"
className="form-control"
placeholder="Username"
onChange={e => setValue(e.target.value)}
/>
</td>
<button onClick={() => setIsEditable(false)}>CANCEL</button>
</>
) : null}
<td>
<button
onClick={() => {
editList({
id: item.id,
username: item.username,
newValue: value
});
setIsEditable(true);
}}
>
Edit
</button>
<button onClick={() => deleteList(item.id)}>Delete</button>
</td>
</tr>
))}
</table>
</div>
</div>
);
};
export default UserGrid;