This is my auth context
import { createContext } from "react";
const AuthContext = createContext();
export default AuthContext;
This is my auth reducer
import { AUTH_ERROR, LOGIN_SUCCESS } from "../types";
export default (state, action) => {
switch (action.type) {
case LOGIN_SUCCESS:
localStorage.setItem("token", action.payload.data.token);
return {
...state,
isAuthenticated: true,
userName: action.payload.data.name,
avatar: action.payload.data.avatar,
token: action.payload.data.token,
error: null,
};
case AUTH_ERROR:
localStorage.removeItem("token");
return {
...state,
isAuthenticated: false,
userName: null,
avatar: null,
error: action.payload.message,
};
default:
return state;
}
};
Here is my auth state
import React, { useReducer } from "react";
import axios from "axios";
import AuthContext from "./AuthContext";
import AuthReducer from "./AuthReducer";
import { AUTH_ERROR, LOGIN_SUCCESS } from "../types";
const AuthState = ({ children }) => {
const initialState = {
isAuthenticated: false,
userName: null,
avatar: null,
token: null,
error: null,
};
const [state, dispatch] = useReducer(AuthReducer, initialState);
const login = async (formData) => {
const header = {
headers: {
"Content-Type": "application/json",
},
};
try {
const res = await axios.post("/api/tenant/auth/login", formData, header);
dispatch({
type: LOGIN_SUCCESS,
payload: res.data,
});
} catch (err) {
dispatch({
type: AUTH_ERROR,
payload: err.response.data,
});
}
};
return (
<AuthContext.Provider
value={{
token: state.token,
isAuthenticated: state.isAuthenticated,
userName: state.userName,
avatar: state.avatar,
error: state.error,
login,
}}
>
{children}
</AuthContext.Provider>
);
};
export default AuthState;
In my app.js I have wrapped around the components like this;
function App() {
return (
<AuthState>
<AlertState>
<SettingsState>
<Alert />
<Router>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/login" exact component={Login} />
<Route path="/dashboard" exact component={Dashboard} />
<Route path="/main-settings" exact component={MainSettings} />
</Switch>
</Router>
</SettingsState>
</AlertState>
</AuthState>
);
}
Then in my login component, I tried to use the data from the auth context provider like this;
const Login = ({ history }) => {
const { login, error, userName, isAuthenticated } = useContext(AuthContext);
const { setAlert } = useContext(AlertContext);
return (
<Formik
initialValues={{ email: "", password: "" }}
validationSchema={Yup.object({
email: Yup.string().email("Invalid email address").required("Required"),
password: Yup.string().required("Required"),
})}
onSubmit={async (values, { resetForm, setSubmitting }) => {
const { email, password } = values;
await login({
email,
password,
});
console.log(error, userName, isAuthenticated);
}}
>
{(formik) => (
<>
<div className="container">
<div className="row justify-content-center">
<div className="col-md-8 col-lg-6 col-xl-5">
<div className="card mt-5 shadow">
<div className="card-body p-4" id="error-body">
<div className="text-center mb-4">
<h4 className="text-uppercase mt-0">Sign In</h4>
</div>
<form id="login-form" onSubmit={formik.handleSubmit}>
<div className="form-group">
<label htmlFor="email" className="float-left">
Email <span className="text-danger">*</span>
</label>
<input
type="text"
className="form-control"
id="email"
placeholder=""
name="email"
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.email}
/>
{formik.touched.email && formik.errors.email ? (
<div className="text-danger">
{formik.errors.email}
</div>
) : null}
</div>
<div className="form-group">
<label htmlFor="password" className="float-left">
Password <span className="text-danger">*</span>
</label>
<input
type="password"
className="form-control"
id="password"
placeholder="Password"
name="password"
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.password}
/>
{formik.touched.password && formik.errors.password ? (
<div className="text-danger">
{formik.errors.password}
</div>
) : null}
</div>
<div className="form-group">
<button
className="btn btn-primary btn-block"
type="submit"
style={{
backgroundColor: "#007bff",
borderColor: "#007bff",
}}
>
Log In
</button>
</div>
<hr />
<div className="form-group mb-3 d-flex justify-content-between">
<div className="custom-control custom-checkbox">
<input
type="checkbox"
className="custom-control-input"
id="rememberMe"
value="remember"
/>
<label
className="custom-control-label"
htmlFor="rememberMe"
>
Remember Me
</label>
</div>
<h6>Forgot Password?</h6>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</>
)}
</Formik>
);
};
After submission, I check in the developer tools and I see the context provider has been updated but whenever I use the useContext to access the data, I get the initial data in the state from the console. I think somewhere my component is not being rerendered after making the initial request and updating the state. So, on a subsequent request that is when I get the updated value. What am I doing wrong and how do I, therefore, get the updated value on my first request?
Try moving the console.log(error, userName, isAuthenticated); out from the onSubmit function and place it under this line : const { setAlert } = useContext(AlertContext); , do you now see the updated context values?
Related
I have a React project and the Portals were implemented. Profile.js is the children component of Modal.js. My question is when I run npm start locally, Portals works fine (Profile component can be toggled and it pops up). However, after I deploy the code to gh-pages, the portal doesn't show up (when I click profile, nothing pops up). But when looking at the console, it shows the modal div is inserted. I am wondering how I can fix it? I tried on multiple browsers with the same issue. Thank you
App.js
import './App.css';
import React, { useState, useEffect, Suspense } from 'react';
import {
BrowserRouter as Router,
Switch,
Route
} from "react-router-dom";
import 'tachyons';
import Navigation from './components/Navigation/Navigation';
import Slide from './components/Slide/Slide';
import Modal from './components/Modal/Modal';
import Profile from './components/Profile/Profile';
const Ingredient = React.lazy(() => import('./components/Ingredient/Ingredient'));
const RecommendRecipe = React.lazy(() => import('./components/RecommendRecipe/RecommendRecipe')) ;
const Register = React.lazy(() => import('./components/Register/Register'));
const Signin = React.lazy( ()=> import('./components/Signin/Signin')) ;
const SavedRecipes = React.lazy(() => import('./components/SavedRecipes/SavedRecipes')) ;
const initialState = {
isLogin: false,
user: {
email: '',
username: '',
create_on: '',
user_id: '',
dob: '',
recipes_number: ''
}
}
function App() {
const [ user, setUser ] = useState(initialState);
const [ isProfileOpen, setIsProfileOpen] = useState(false);
useEffect(() => {
const token = window.localStorage.getItem('token')
if (token) {
return fetch('https://warm-reef-43761.herokuapp.com/signin', {
method: 'post',
headers: {
'content-type': 'Application/json',
'authorization': token
}
})
.then(resp => resp.json())
.then(data => {
if (data.id) {
return fetch(`https://warm-reef-43761.herokuapp.com/profile/${data.id}`, {
method: 'get',
headers: {
'Content-Type': 'application/json',
'authorization': token
}
})
.then(resp => resp.json())
.then(user => {
loadUser(user)
})
.catch(err => console.log('unable to refresh'))
}
})
.catch(console.log)
}
}, [])
const loadUser = (data) => {
setUser({
isLogin: true,
user: {
email: data.email,
username: data.username,
create_on: data.create_on,
user_id: data.user_id,
dob: data.dob,
recipes_number: data.recipes_number
}
});
}
const signout = (event) => {
setUser(initialState)
window.localStorage.removeItem('token')
}
const toggleModal = () => {
setIsProfileOpen(!isProfileOpen)
}
return (
<div className="App">
<Router basename={process.env.PUBLIC_URL}>
<Navigation
isLogin={user.isLogin} signout={signout} toggleModal={toggleModal}/>
{ isProfileOpen &&
<Modal>
<Profile toggleModal={toggleModal} user={user.user} loadUser={loadUser}/>
</Modal>
}
<Suspense fallback={<span>Loading</span>}>
<Switch>
<Route path='/' exact component = {Slide} />
<Route path='/register' >
<Register loadUser={loadUser} />
</Route>
<Route path='/signin'>
<Signin loadUser={loadUser} />
</Route>
<Route path='/foodlist' >
<Ingredient isLogin={user.isLogin}/>
</Route>
<Route path='/recommendrecipes' >
<RecommendRecipe user={user.user} loadUser={loadUser} isLogin={user.isLogin} />
</Route>
<Route path='/savedrecipes' >
<SavedRecipes user={user.user} loadUser={loadUser}/>
</Route>
</Switch>
</Suspense>
</Router>
</div>
);
}
export default App;
Modal.js
import { Component } from 'react';
import ReactDOM from 'react-dom';
import './Modal.css'
const modalRoot = document.getElementById('modal-root');
class Modal extends Component {
constructor(props) {
super(props)
this.el = document.createElement('div')
}
componentDidMount() {
modalRoot.appendChild(this.el)
}
componentWillUnmount() {
modalRoot.removeChild(this.el)
}
render() {
return ReactDOM.createPortal(this.props.children, this.el)
}
}
export default Modal;
Profile.js
import Button from 'react-bootstrap/Button';
import { useState } from 'react';
import './Profile.css'
function Profile({ toggleModal, user, loadUser }) {
const [ username, setUsername ] = useState(user.username);
const [ dob, setDob ] = useState(user.dob)
const onInputChange = (event) => {
switch (event.target.name) {
case 'username':
setUsername(event.target.value)
break
case 'date':
setDob(event.target.value)
break
default:
return
}
}
const onEditButton = () => {
setDob(null)
}
const onSaveSubmit = (data) => {
fetch(`https://warm-reef-43761.herokuapp.com/profile/${user.user_id}`, {
method: 'post',
headers: {
'Content-Type': 'application/json',
'authorization': window.localStorage.getItem('token')
},
body: JSON.stringify({ formUpdate: data
})
})
.then(resp => resp.json())
.then(data => {
loadUser({...user, ...data})
toggleModal()
})
.catch(err => console.log(err))
}
return (
<div className='modal'>
<div className="col-md-8">
<div className="card mb-3">
<div className="card-body">
<div className="row">
<div className="col-sm-11" >
</div>
<div className="col-sm-1 mb-3">
<Button onClick={toggleModal} variant="info" size='sm'>x</Button>
</div>
</div>
<div className="row">
<div className="col-sm-3 align-self-center">
<h6 className="mb-0">Username</h6>
</div>
<div className="col-sm-7">
<input onChange={onInputChange} name='username' placeholder={user.username}/>
</div>
</div>
<hr />
<div className="row">
<div className="col-sm-3 align-self-center">
<h6 className="mb-0">Email</h6>
</div>
<div className="col-sm-9 ">
<h6> {user.email} </h6>
</div>
</div>
<hr />
<div className="row">
<div className="col-sm-3 align-self-center">
<h6 className="mb-0">Date of Birth</h6>
</div>
{ dob === undefined || dob === null ?
<div className="col-sm-9 align-self-center">
<input type='date' name='date' onChange={onInputChange} />
</div>
:
<>
<div className="col-sm-7 align-self-center">
<h6>{new Date(dob).toLocaleDateString()}</h6>
</div>
<div className="col-sm-2">
<Button variant="info" onClick={onEditButton}>Edit</Button>
</div>
</>
}
</div>
<hr />
<div className="row">
<div className="col-sm-3" >
<h6 className="mb-0">Saved Recipes</h6>
</div>
<div className="col-sm-9 ">
{user.recipes_number}
</div>
</div>
<hr />
<div className="row">
<div className="col-sm-3">
<h6 className="mb-0">Register on</h6>
</div>
<div className="col-sm-9 ">
{new Date(user.create_on).toLocaleDateString()}
</div>
</div>
<hr />
<div className="row">
<div className="col-sm-12">
<button className="btn btn-info ma2" onClick={() => onSaveSubmit({username, dob})}>Save</button>
<button className="btn btn-info ma2 " onClick={toggleModal}>Close</button>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
export default Profile;
trying to use the handle submit function to change the isAuthinticated to true from false thats on the main app.js file thats using react router.. all of the redux examples i look at are all using an app js file that has the onclick function on it not react router. thank you if you can help
function App () {
return (
<Router>
<div>
<Switch>
<Route exact path="/" component={Login} />
<Route exact path="/login" component={Login} />
<Route exact path="/signup" component={Signup} />
<PrivateRoute
exact path="/mainpage"
component={MainPage}
isAuthenticated={false}
/>
</Switch>
<Footer />
</div>
</Router>
);
}
export default App;
my login.js that has the click event
const Login = () => {
const history = useHistory()
const [state, setState] = useState({
email: '',
password: ''
});
const [validate, setValid] = useState({
validateEmail: '',
validatePassword: '',
})
const handleInputChange = event => setState({
...state,
[event.target.name]: event.target.value,
})
const handleSubmit = user => {
if(state.password === ''){
setValid({validatePassword:true})
}
if(state.email === '' ){
setValid({validateEmail:true})
}
axios.post(`auth/login`, state )
.then(res => {
console.log(res);
console.log(res.data);
if (res.status === 200 ) {
history.push('/mainpage');
}
})
.catch(function (error) {
console.log(error);
alert('Wrong username or password')
window.location.reload();
});
}
// console.log('state', state)
const {email, password } = state
const [popoverOpen, setPopoverOpen] = useState(false);
const toggle = () => setPopoverOpen(!popoverOpen);
return (
<>
<Container>
<Row>
<Col>
<img src={logo} alt="Logo" id="logo" />
<Button id="Popover1" type="button">
About Crypto-Tracker
</Button>
<Popover placement="bottom" isOpen={popoverOpen} target="Popover1" toggle={toggle}>
<PopoverHeader>About</PopoverHeader>
<PopoverBody>Your personalized finance app to track all potential cryptocurrency investments</PopoverBody>
</Popover>
</Col>
<Col sm="2" id="home" style={{height: 500}}>
<Card body className="login-card">
<Form className="login-form">
<h2 className="text-center">Welcome</h2>
<h3 className="text-center">____________</h3>
<FormGroup>
<Label for="exampleEmail">Email</Label>
<Input invalid={validate.validateEmail} onChange = {handleInputChange} value = {email} type="email" required name="email" placeholder="email" />
<FormFeedback>Please enter email</FormFeedback>
</FormGroup>
<FormGroup>
<Label for="examplePassword">Password</Label>
<Input invalid={validate.validatePassword} onChange = {handleInputChange} value = {password} type="password" required name="password" placeholder="password"/>
<FormFeedback>Please enter password</FormFeedback>
</FormGroup>
<Button onClick={()=> handleSubmit(state)} className="but-lg btn-dark btn-block">Login</Button>
<div className="text-center pt-3"> or sign in with Google account</div>
<Loginbutton />
<div className="text-center">
Sign up
<span className="p-2">|</span>
Forgot password
</div>
</Form>
</Card>
</Col>
</Row>
</Container>
</>
);
}
export default Login;
authslice.js using asyncthunk
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit'
import { userAPI } from '../../../server/routes/authRoutes'
const fetchAuth = createAsyncThunk(
'auth/login',
async (userId, thunkAPI) => {
const response = await userAPI.fetchById(userId)
return response.data
}
)
const authSlice = createSlice({
name: 'isAuthenticated',
initialState: {
value: false,
},
reducers: {
// Authenticated: state => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
// doesn't actually mutate the state because it uses the Immer library,
// which detects changes to a "draft state" and produces a brand new
// immutable state based off those changes
// state.value = true;
// },
},
extraReducers: {
[fetchAuth.fulfilled]: state => {
// Add user to the state array
state.value = true
},
[fetchAuth.rejected]: state => {
// Add user to the state array
state.value = false
}
}
});
dispatch(fetchUserById(123))
// export const { increment, decrement, incrementByAmount } = counterSlice.actions;
This would be easy to achieve if you use some kind of global store to store such values. Typical options include Redux, Mobx, etc.
Define your isAuthenticated variable as a global store variable. mutate its value using corresponding package methods like Redux dispatch events or similar from your functions anywhere else in the app.
You can try it using Redux, creating a store to the login variables, and sharing it with the component App.js.
I have a component called admin it is a form that will redirect me to another page rendering not working though.
Router main
const globalState = {
isAuthed: false,
token: null,
};
export const AuthContext = React.createContext(globalState);
function App() {
const [currentUser, setCurrentUser] = useState(globalState)
return (
<AuthContext.Provider value={[currentUser, setCurrentUser]}>
<Router>
<Switch>
<Route exact path="/admin" component={Admin} />
<Route exact path="/admin-panel" component={Pannel} />
</Switch>
</Router>
</AuthContext.Provider>
)
}
export default App;
admin component
const LoginForm = () => {
const [state, setState] = useContext(AuthContext)
const login = (state) => {
const user = document.getElementById('user').value;
const pass = document.getElementById('pass').value;
const request = {
user,
pass
}
console.log(request)
fetch('/api/admin', {
method: "post",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(request),
})
.then(res => res.json())
.then(res => {
if(res.auth){
valid(5000,"Login Success. Redirecting in 3 second")
setTimeout(() =>{
setState({isAuthed: res.auth, token: res.key})
}, 3000)
}
else{
warn(5000,res.message)
}
})
}
return(
<div style={css}>
<ToastContainer />
{(state && state.isAuthed)? <Redirect to='/adming-panel'/>: false}
<h1 style={{color: "teal"}}>Admin Panel</h1>
<Form id="login-form" size='large' style={{backgroundColor: "white"}}>
<Segment stacked>
<Form.Input id="user" fluid icon='user' iconPosition='left' placeholder='E-mail address' />
<Form.Input
fluid
icon='lock'
iconPosition='left'
placeholder='Password'
type='password'
id="pass"
/>
<Button onClick={() => login(state)} color='teal' fluid size='large'>
Login
</Button>
</Segment>
</Form>
</div>
)
}
export default LoginForm
the new page that I want to render
const Pannel = () => {
const [state, setState] = useContext(AuthContext)
return (
<div>
{(!state || !state.isAuthed)? <Redirect to='/adming-panel'/>: false}
Secret Page
</div>
)
}
export default Pannel
All the answers that I searched for. Was to put the exact keyword before the path but still, the component won't render only an empty white screen appears and no errors on console or backend console.
<Route exact path="/admin-panel" component={Pannel} />
Spot the key difference
<Redirect to='/adming-panel'/>
You are welcome.
I have a big issue about updating the comment of a post. The thing is the client don't want it to be public so default is no show. I have this code
import React, { useState, useEffect, Fragment } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { getPost } from '../../actions/post';
// Other parts
import ScrollToTop from '../routing/ScrollToTop';
const CommentEdition = ({ getPost, post: { post, loading }, match }) => {
const [formData, setFormData] = useState({
cstatus: false,
comment: '',
});
useEffect(() => {
getPost(match.params.id);
}, [getPost, match.params.id]);
const { cstatus, comment } = formData;
const onChange = (e) =>
setFormData({ ...formData, [e.target.name]: e.target.value });
return (
<section className='section-size-2 lighter-bg'>
<ScrollToTop />
<div className='container'>
<div className='grid'>
<div className='column-4 '>
<Link
to='/user-comments'
className='white-button'
style={{ marginBottom: 30, display: 'block' }}
>
Back to Comments
</Link>
<h4>Comments management</h4>
<h1 className='animated-text'>Review & Edit</h1>
</div>
<div className='column-8 profile-main-area'>
<Fragment>
<form className='box white shadow text-left'>
<label>Did you become a client already? *</label>
<div className='form-input-select'>
<select
id='cstatus'
name='cstatus'
value={cstatus}
onChange={(e) => onChange(e)}
>
<option value='true'>Yes</option>>
<option value='false'>No</option>
</select>
<i className='fas fa-chevron-down'></i>
</div>
<label>About You</label>
<textarea
name='comment'
placeholder='Tell us about you'
value={comment}
onChange={(e) => onChange(e)}
></textarea>
<button className='button' type='submit'>
Submit
</button>
</form>
</Fragment>
</div>
</div>
</div>
</section>
);
};
CommentEdition.propTypes = {
getPost: PropTypes.func.isRequired,
post: PropTypes.object.isRequired,
};
const mapStateToProps = (state) => ({
post: state.post,
});
export default connect(mapStateToProps, { getPost })(CommentEdition);
It comes from clicking on the specific comment I want to publish, but I don't have any idea how I would put the info in the form. I have getPost(match.params.id) and is showing the right post, but how can I get the that specific comment and populate in the form for update.
The link looks like this http://localhost:3000/review-commment/5e806b4d6de9c747939a1696/5e9f4ff01c70d30300c42feb
Thanks for your help, if this is too complicated and need more details please let me know.
I'm attaching an image so you can see how the two comments are loading in the state.
NEW SCREENSHOT
CODE UPDATED AND NEW SCREENSHOT:
import React, { useState, useEffect, Fragment } from 'react';
import PropTypes from 'prop-types';
import { connect, useSelector } from 'react-redux';
import { Link } from 'react-router-dom';
import { getPost } from '../../actions/post';
// Other parts
import ScrollToTop from '../routing/ScrollToTop';
const CommentEdition = ({ getPost, post: { post, loading }, match }) => {
const [formData, setFormdata] = useState({
cstatus: false,
comment: ''
});
const { comments } = useSelector(state => ({ ...state.post.post }));
const curco =
comments && comments.filter(el => el._id === match.params.commentid);
console.log(curco);
useEffect(() => {
getPost(match.params.id);
}, [getPost, match.params.id]);
const { cstatus, comment } = formData;
const onChange = e =>
setFormdata({ ...formData, [e.target.name]: e.target.value });
return (
<section className="section-size-2 lighter-bg">
<ScrollToTop />
<div className="container">
<div className="grid">
<div className="column-4 ">
<Link
to="/user-comments"
className="white-button"
style={{ marginBottom: 30, display: 'block' }}
>
Back to Comments
</Link>
<h4>Comments management</h4>
<h1 className="animated-text">Review & Edit</h1>
</div>
<div className="column-8 profile-main-area">
<Fragment>
<form className="box white shadow text-left">
<label>Comment Status *</label>
<div className="form-input-select">
<select
id="cstatus"
name="cstatus"
value={cstatus}
onChange={e => onChange(e)}
>
<option value="true">Published</option>>
<option value="false">Draft</option>
</select>
<i className="fas fa-chevron-down"></i>
</div>
<label>Comment</label>
<textarea
name="comment"
placeholder="Comment goes here"
value={comment}
onChange={e => onChange(e)}
></textarea>
<button className="button" type="submit">
Submit
</button>
</form>
</Fragment>
</div>
</div>
</div>
</section>
);
};
CommentEdition.propTypes = {
getPost: PropTypes.func.isRequired,
post: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
post: state.post,
curco: state.curco
});
export default connect(mapStateToProps, { getPost })(CommentEdition);
I wanted to add the code from where these comments are coming, maybe that could help better.
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import Moment from 'react-moment';
import { connect } from 'react-redux';
const CommentItem = ({ auth, post: { _id, title, comments, date } }) => {
return (
<Fragment>
{auth.user.usertype === 'worker' && comments.length > 0 ? (
<div
className="quotee-post comment-list"
style={{
borderBottom: '2px solid #e6e6e6',
paddingBottom: 25,
marginBottom: 25
}}
>
<Fragment>
<div className="title">List of comments for</div>
<h4>{title}</h4>
{comments.map((comment, id) => (
<div key={id} className="caption comments-data">
<div className="first-col">
<h6>{comment.comment}</h6>
<Fragment>
<div className="sub">By {comment.name}</div>
<p>
Date <Moment format="MM/DD/YYYY">{comment.date}</Moment>
</p>
</Fragment>
</div>
<div className="second-col">
{comment.cstatus ? (
<Fragment>
<small>
Comment Status:
<br />
<span style={{ color: '#28a745' }}>
<i className="fas fa-check"></i> published
</span>
</small>
</Fragment>
) : (
<Fragment>
<small>
Comment Status:
<br />
<span style={{ color: '#fe9100' }}>
<i className="fas fa-pause-circle"></i> draft
</span>
</small>
</Fragment>
)}
<br />
<Link
className="red-button"
to={`/review-commment/${_id}/${comment._id}`}
>
Review and Edit
</Link>
</div>
</div>
))}
</Fragment>
</div>
) : null}
</Fragment>
);
};
CommentItem.propTypes = {
post: PropTypes.object.isRequired,
auth: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
auth: state.auth
});
export default connect(mapStateToProps, {})(CommentItem);
Screenshot showing the console.
You can get the comment a list of object from store using useSelector
You can add the following code to get a list of comment object and start filtering with id to get the specific comment
const comment = useSelector(state => state.post.comments)
// I can see that state are not parent of comments, you might want to add the parent in above like
// useSelector(state => state.posts.comments)
// In this case, posts is the parent of comments
const filteredComment = comment && comment.filter(el => el._id === match.params.id)
// filteredComment.comment should be the comment you needed
Updated answer:
<textarea
name="comment"
placeholder="Comment goes here"
initialValue = {curco ? curco[0].comment || undefined}
value={comment}
onChange={e => onChange(e)}
></textarea>
I have been a little busy, but here's is the solution to this issue. The actual solution was really cleaner, what I did is just load the info here
import React, { useState, useEffect, Fragment } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { getPost, updateComment } from '../../actions/post';
// Other parts
import ScrollToTop from '../routing/ScrollToTop';
const CommentEdition = ({
getPost,
post: { post, loading },
location: { currentcom },
postId,
commentId,
updateComment,
match
}) => {
const [formData, setFormdata] = useState({
cstatus: false,
comment: '',
user: '',
name: '',
photo: ''
});
useEffect(() => {
getPost(match.params.id);
const newcstatus = currentcom && currentcom.cstatus;
const newcomment = currentcom && currentcom.comment;
const newuser = currentcom && currentcom.user;
const newphoto = currentcom && currentcom.photo;
const newname = currentcom && currentcom.name;
setFormdata({
cstatus: newcstatus,
comment: newcomment,
user: newuser,
photo: newphoto,
name: newname
});
}, [getPost, currentcom, match.params.id]);
const { cstatus, comment, user, photo, name } = formData;
const onChange = e =>
setFormdata({ ...formData, [e.target.name]: e.target.value });
const onSubmit = e => {
e.preventDefault();
postId = match.params.id;
commentId = match.params.commentid;
updateComment(postId, commentId, formData);
};
return (
<section className="section-size-2 lighter-bg">
<ScrollToTop />
<div className="container">
<div className="grid">
<div className="column-4 ">
<Link
to="/user-comments"
className="white-button"
style={{ marginBottom: 30, display: 'block' }}
>
Back to Comments
</Link>
<h4>Comments management</h4>
<h1 className="animated-text">Review & Edit</h1>
</div>
<div className="column-8 profile-main-area">
<Fragment>
<form
onSubmit={e => onSubmit(e)}
className="box white shadow text-left"
>
<label>Comment Status *</label>
<div className="form-input-select">
<select
id="cstatus"
name="cstatus"
value={cstatus}
onChange={e => onChange(e)}
>
<option value="true">Published</option>>
<option value="false">Draft</option>
</select>
<i className="fas fa-chevron-down"></i>
</div>
<label>Comment</label>
<textarea
name="comment"
placeholder="Comment goes here"
value={comment}
onChange={e => onChange(e)}
></textarea>
<label>Author of comment</label>
<input name="name" type="text" value={name} readOnly />
<label>User</label>
<input name="user" type="text" value={user} readOnly />
<label>Photo file</label>
<input name="photo" type="text" value={photo} readOnly />
<button className="button" type="submit">
Submit
</button>
</form>
</Fragment>
</div>
</div>
</div>
</section>
);
};
CommentEdition.propTypes = {
getPost: PropTypes.func.isRequired,
updateComment: PropTypes.func.isRequired,
post: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
post: state.post
});
export default connect(mapStateToProps, { getPost, updateComment })(
CommentEdition
);
What really makes it easier is that from the other component I loaded the props onto this one, then this one it turns to be just a regular form mounting the data from props, but when I save I had to splice that comment.
<Link
className="red-button"
to={{
pathname: `/review-commment/${_id}/${comment._id}`,
currentcom: comment
}}
>
Review and Edit
</Link>
I am trying to take input from a user and pass that input into an axios get, when this is done the resultant information is passed into an array that will be displayed as cards.
I am having an issue where the code below is compiling but when enter values into the form fields and press submit, nothing occurrs. Nothing shows up in the web console either.
Where abouts am I going wrong?
const initial_state = {
location: "",
cuisine: "",
searchResults: []
};
class SearchMealForm extends Component {
constructor(props) {
super(props);
this.state = { ...initial_state };
}
//handle user input and inject it into yelp api get request
handleSubmit = event => {
event.preventDefault();
const { location, cuisine, searchResults} = this.state;
this.props.onFormSubmit(this.state(location, cuisine, searchResults));
axios.get(`https://api.yelp.com/v3/businesses/search?location=${location}+IE&categories=${cuisine}`)
.then(response => this.setState({searchResults}))
};
handleChange = event => {
this.setState({ [event.target.name]: event.target.value });
};
//YELP http api get request
searchYelpRestaurants = (location, cuisine, searchResults) => {
axios
.get(
`https://api.yelp.com/v3/businesses/search?location=${location}+IE&categories=${cuisine}`,
{
headers: {
Authorization: `Bearer ${process.env.REACT_APP_API_KEY_YELP}`
}
}
)
.then(res => this.setState({ searchResults: res.data.businesses }));
};
render() {
const { location, cuisine } = this.state;
//create cards with the results from the Yelp API GET
const YelpSearchResults = this.state.searchResults.map(result => {
return (
<div className="ResultCard">
<Card style={{ width: "18rem" }}>
<Card.Img variant="top" src={result.image_url} />
<Card.Body>
<Card.Title>{result.name}</Card.Title>
</Card.Body>
<ListGroup className="list-group-flush">
<ListGroupItem>{result.categories}</ListGroupItem>
<ListGroupItem>{result.rating}</ListGroupItem>
</ListGroup>
<Button variant="primary">Book restaurant</Button>
</Card>
</div>
);
});
// return YelpSearchResults;
// }
return (
<React.Fragment>
<div className="SearchMeal">
<Form onSubmit={this.handleSubmit}>
<Form.Row>
<Form.Group as={Col}>
<Form.Label>City</Form.Label>
<Form.Control
name="location"
type="text"
value={location}
onChange={this.handleChange}
placeholder="location"
/>
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col}>
<Form.Label>Cuisine</Form.Label>
<Form.Control
name="cuisine"
type="text"
value={cuisine}
onChange={this.handleChange}
placeholder="cuisine"
/>
</Form.Group>
</Form.Row>
<Button>Submit</Button>
<Button>Clear</Button>
</Form>
</div>
{YelpSearchResults}
</React.Fragment>
);
}
}
export default SearchMealForm;
here Button type should be specified as submit
<Button type="submit">Submit</Button>
in order for form submit to work!
I would refactor your component to be functional
import React, { useState } from "react";
import ReactDOM from "react-dom";
import axios from "axios";
import {
Col,
Form,
Card,
Button,
ListGroup,
ListGroupItem
} from "react-bootstrap";
const initial_state = {
location: "",
cuisine: "",
searchResults: []
};
const SearchMealForm = ({ onFormSubmit = () => {} }) => {
const [state, setState] = useState(initial_state);
//handle user input and inject it into yelp api get request
const handleSubmit = async event => {
event.preventDefault();
const { location, cuisine } = state;
onFormSubmit(state);
const searchResults = await searchYelpRestaurants({ location, cuisine });
setState({ ...state, searchResults });
};
const handleChange = event => {
setState({
...state,
[event.target.name]: event.target.value
});
};
//YELP http api get request
const searchYelpRestaurants = async ({ location, cuisine }) => {
try {
const { data: { businesses: searchResults } = {} } = await axios.get(
`https://api.yelp.com/v3/businesses/search?location=${location}+IE&categories=${cuisine}`,
{
headers: {
Authorization: `Bearer dBMqyRFmBBg7DMZPK9v3rbGHmrLtlURpNUCJP6gbYHtyHTmboF-Mka-ZkHiDNq-G9ktATohJGD5iKQvelOHg3sDorBDiMgnsaa8SzTH8w6hjGQXlaIexDxFlTW3FXXYx`
}
}
);
return searchResults;
} catch (err) {
console.error(err);
}
return [];
};
const { location, cuisine, searchResults } = state;
//create cards with the results from the Yelp API GET
const YelpSearchResults = searchResults.map(result => (
<div className="ResultCard">
<Card style={{ width: "18rem" }}>
<Card.Img variant="top" src={result.image_url} />
<Card.Body>
<Card.Title>{result.name}</Card.Title>
</Card.Body>
<ListGroup className="list-group-flush">
<ListGroupItem>{result.categories}</ListGroupItem>
<ListGroupItem>{result.rating}</ListGroupItem>
</ListGroup>
<Button variant="primary">Book restaurant</Button>
</Card>
</div>
));
return (
<React.Fragment>
<div className="SearchMeal">
<Form onSubmit={handleSubmit}>
<Form.Row>
<Form.Group as={Col}>
<Form.Label>City</Form.Label>
<Form.Control
name="location"
type="text"
value={location}
onChange={handleChange}
placeholder="location"
/>
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col}>
<Form.Label>Cuisine</Form.Label>
<Form.Control
name="cuisine"
type="text"
value={cuisine}
onChange={handleChange}
placeholder="cuisine"
/>
</Form.Group>
</Form.Row>
<Button type="submit">Submit</Button>
<Button>Clear</Button>
</Form>
</div>
{YelpSearchResults}
</React.Fragment>
);
};
export default SearchMealForm;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>