React Js Reversed array un reverses on clicks - javascript

So I have an array that gets pulled from a database and I have reversed it so that the newest db entry goes to the top when you click a button on the page which opens a little modal style window the displayed array flips and goes from oldest to newest for some reason and I don't know why.
here is the code:
import React, { Component } from 'react';
import axios from 'axios';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import RaisedButton from 'material-ui/RaisedButton';
import { Modal, ModalHeader, ModalTitle, ModalClose, ModalFooter, ModalBody } from 'react-modal-bootstrap';
import TextField from 'material-ui/TextField';
class ViewNoteDetails extends Component {
constructor(props) {
super(props);
this.NewNote = this.NewNote.bind(this);
this.handleChange = this.handleChange.bind(this);
}
state = {
Case: this.props.Case,
notesToShow: this.props.Case.slice(0, parseInt(this.props.AmountToShow, 10)),
isOpen: false,
note: '',
}
NewNote() {
const self = this;
if (this.state.note !== '') {
axios.post('http://******'
+ window.location.href.split('view/')[1]
+ '/' + self.state.note
).then(function (res) {
if (res.data === "Updated") {
self.hideModal();
window.location.reload();
}
})
.catch(function (error) {
console.log(error);
});
} else {
window.alert("Cannot Enter A Blank Note")
}
}
handleChange(e) {
this.setState({
[e.target.name]: e.target.value
});
}
openModal = () => {
this.setState({
isOpen: true
});
};
hideModal = () => {
this.setState({
isOpen: false
});
};
render() {
const pullRight = {
float: "right",
display: "inline",
}
const inline = {
display: "inline",
}
const Overflow = {
overflow: "auto",
width: "100%",
height: "50vw",
}
return (
<MuiThemeProvider>
<div>
<Modal isOpen={this.state.isOpen} onRequestHide={this.hideModal}>
<ModalHeader>
<ModalClose onClick={this.hideModal} />
<ModalTitle>Enter Your Note</ModalTitle>
</ModalHeader>
<ModalBody>
<TextField hintText="Enter Note" name="note" fullWidth={true} onChange={this.handleChange} />
</ModalBody>
<ModalFooter>
<RaisedButton label="Save Note" secondary={true} onClick={() => this.NewNote()} />
<RaisedButton label="Cancel Note" secondary={true} onClick={() => this.hideModal()} />
</ModalFooter>
</Modal>
<br />
<h1 id="TOP" style={inline}>Note Details</h1>
<RaisedButton label="New Note" style={pullRight} secondary={true} onClick={() => this.openModal()} />
<br />
<br />
<div style={Overflow}>
{this.state.notesToShow.reverse().map(function (note, index) {
return (
<div key={note.NotePK}>
<p className="well well-lg">
{note.CreationDate.split('T')[0]} # {note.CreationDate.split('T')[1].split('.')[0]} : {note.Note}
</p>
</div>
);
})}
</div>
<br />
</div>
</MuiThemeProvider>
);
}
}
***** is in replacement of the database location
when you click the new note button the order reverses on the rendered notes
so if it was:
note 1
note 2
note 3
on click of new note it would change to
note 3
note 2
note 1
why does it do this and how do i fix this.

Don't reverse it in your render method. Every time it gets re-rendered you will reverse it. So what happens is probably is that it is getting reversed a second time when you open the modal window.
Do it here for once
state = {
Case: this.props.Case,
notesToShow: this.props.Case.slice(0, parseInt(this.props.AmountToShow, 10)).reverse(),
isOpen: false,
note: '',
}

Related

I want error object in a parent from child in formk?

It is my parent component
<FormContainer
initialFieldValues={id != undefined ? cvRecord : cvFormFields}
formData={getFieldsByCategory(cvFormFields, selectOption.type)}
formValidation={cvFormFields}
// onFormSubmit={onFormSubmit}
>
{/* button group */}
<div className="text-md-end mt-5">
<button className="btn btn-primary ms-2 mb-2" type="submit">
Save <i className="bi bi-file-earmark-fill" />
</button>
<button
type="button"
className="btn btn-danger ms-2 mb-2"
data-bs-toggle="modal"
data-bs-target="#cvdownloadmodal"
>
<i className="bi bi-eye me-1" />
Preview and Download
</button>
</div>
</FormContainer>
and this is child component
import { Form, Formik, useFormikContext } from "formik";
import React, { useMemo } from "react";
import GenerateFields from "./GenerateFields";
import { getFormInitialValue, getFormValidations } from "./utility/formUtils";
import * as Yup from "yup";
import { toast } from "react-toastify";
const FormContainer = ({
initialFieldValues = [],
formData = [],
onFormSubmit = "",
formValidation = [],
extraInputClass = "",
children,
...otherProps
}) => {
// Toggle Validations
const withValidation = formValidation ? true : false;
// Get form Initial Value
const initialValues = Array.isArray(initialFieldValues)
? getFormInitialValue(initialFieldValues)
: initialFieldValues;
// Get Form Validaton Schema
const validationSchema = Yup.object().shape(
getFormValidations(formValidation)
);
return (
<Formik
initialValues={initialValues}
validationSchema={withValidation ? validationSchema : ""}
onSubmit={
onFormSubmit
? onFormSubmit
: (val, { errors }) => {
console.log("🚀 ~ file: FormContainer.jsx:38 ~ val", errors);
setFormValues([...formValues, val]);
}
}
enableReinitialize={true}
>
{(formAttributes) => (
<Form className="formikContainer row m-2">
{formData.map((fieldAttribute, index) => {
return (
<GenerateFields
key={index}
inputAttributes={fieldAttribute}
formActions={formAttributes}
withValidation={withValidation}
/>
);
})}
{children && children}
</Form>
)}
</Formik>
);
};
export default FormContainer;
and this is my output
my result screen
Now I want to get an error object in a parent component that was used to show a red area in which validation is required like if the user only enters the basic info and tries to submit the form then the remaining section becomes red because the user leaves the field empty in the remaining section. Anyone has any idea how to tackle this problem?
I expect the logic which is helpful in my project
import React, { useEffect, useState } from 'react'
function ChildrenComponant({ setError }) {
return (
<div>
{/* error from formik */}
{({ error }) => {
setError(error)
}}
</div>
)
}
function Parent() {
const [error, setError] = useState({ message: "" })
useEffect(() => {
// some function or re-render logic after error change
}, [error])
return (
<div>
{/* show error message */}
<div>{error.message}</div>
{/* Passing setError prop to child so that error can be set from children and accessed in parent */}
<ChildrenComponant setError={setError} />
</div>
)
}
export default Parent

React auto scroll to bottom on a chat container

I am trying to build a chat page using react. And I have obviously come to a problem where the chat bubbles container doesn't automatically scroll down to bottom on componentDidMount and Update.
I was looking through the previous Q&A but couldn't find any decent solution.
Here is the comoponent.
// renders the text form and all the messages
import React, { Component } from 'react';
import { convo } from '../../data/convo';
import SingleMessage from '../singleMessage/singleMessage';
import StyledForm from './styles';
import moment from 'moment';
class MessageRoom extends Component {
//convo contains the messages
state = {
convo,
message: ''
};
handleChange = e => {
const message = e.target.value;
this.setState({ message });
};
onSubmit = e => {
e.preventDefault();
if (this.state.message) {
const text = {
message: this.state.message,
owner: 0,
date: moment()
};
this.setState({ convo: [...this.state.convo, text], message: '' });
}
};
render() {
return (
<StyledForm>
<div className="messages">
{this.state.convo.map(text => (
<SingleMessage text={text} key={text.date} />
))}
</div>
<div>
<form
onSubmit={e => {
this.onSubmit(e);
}}
>
<input
type="text"
placeholder="Type a message"
value={this.state.message}
onChange={this.handleChange}
/>
<button type="submit"> Send </button>
</form>
</div>
</StyledForm>
);
}
}
export default MessageRoom;
So please help a brother out!
// renders the text form and all the messages
import React, { Component } from 'react';
import { convo } from '../../data/convo';
import SingleMessage from '../singleMessage/singleMessage';
import StyledForm from './styles';
import moment from 'moment';
class MessageRoom extends Component {
constructor() {
super();
this.state = {
convo,
message: ''
};
this.mesRef = React.createRef();
}
componentDidMount() {
this.scrollToBottom();
}
scrollToBottom = () => {
this.mesRef.current.scrollTop = this.mesRef.current.scrollHeight;
};
handleChange = e => {
const message = e.target.value;
this.setState({ message });
};
onSubmit = e => {
e.preventDefault();
if (this.state.message) {
const text = {
message: this.state.message,
owner: 0,
date: moment()
};
this.setState(
{ convo: [...this.state.convo, text], message: '' },
() => {
this.scrollToBottom();
}
);
}
};
render() {
return (
<StyledForm>
<div className="messages" ref={this.mesRef}>
{this.state.convo.map(text => (
<SingleMessage text={text} key={text.date} />
))}
</div>
<div>
<form
onSubmit={e => {
this.onSubmit(e);
}}
>
<input
type="text"
placeholder="Type a message"
value={this.state.message}
onChange={this.handleChange}
/>
<button type="submit"> Send </button>
</form>
</div>
</StyledForm>
);
}
}
export default MessageRoom;
updated the code to the new ref usage.
There is a very simple way to achieve it some css trick
Wrap Html into parent div for message
<div className="message-holder">
<div className="message"> //state all message
...text goes here
</div>
</div>
<style>
.message-holder{
position:absolute;
bottom:0;
//if required overflow to scroll add below css
overflow-y:scroll
max-height: //whatever is required
}
.message{
//css style goes here
}
</style>
Have question ping me

How to pass data to a component from sibling component react

My requirement is to re-render header component based on signed in user.
I have header component in which I have a nav called Admin which needs to be visible if admin logs in.
I have other component call signin component in which when admin sign in I need to pass that value to header component such that Admin nav shows up.
My requirement is similar to React: how to pass props from child to parent to another child?
but my signin component is in router.
On successful sign in I am routing user to homepage and setting value in local storage but my header component is not re-rendering.
Is there any way I can achieve this?
Kindly help.
App component
import React, { Component } from 'react';
import '../node_modules/video-react/styles/scss/video-react.scss';
import '../node_modules/bootstrap-vertical-tabs/bootstrap.vertical-tabs.min.css';
import routes from './routes';
import {withRouter} from 'react-router-dom';
import HeaderComponent from './components/header-component/header';
import './App.css';
import Footer from './components/footer/Footer';
import loader from './assets/loader.png';
import { Animated } from 'react-animated-css';
import firebase from "firebase";
class App extends Component {
constructor(props){
super(props);
this.props = props;
this.state = {
authUser:null
}
this.handleSignInHeader = this.handleSignInHeader.bind(this);
}
onSignIn(data){
console.log(data);
}
componentDidMount(){
firebase.auth().onAuthStateChanged(authUser =>{
authUser ? this.setState(() => ({ authUser })) : this.setState(() => ({ authUser: null }));
})
console.log(this.state)
}
handleSignInHeader(){
console.log('Sign handleSignInHeader method triggered')
}
render() {
return (
<div className="App" >
<div className="logo-wrapper ">
<div className="image-wrapper">
<Animated animationIn="bounce infinite" >
<img alt="loader" src={loader}/>
</Animated>
</div>
</div>
<div className="container-fluid">
<div className="row set-min-height"><HeaderComponent/></div>
<div className="row route-container" >
{routes}
</div>
<div className="row">
<Footer/>
</div>
</div>
</div>
);
}
}
export default App;
Sign in component
import React, { Component } from 'react';
import './sign-in.css';
import { Button, Form, FormGroup, Label, Input } from 'reactstrap';
import { auth } from '../../firebase/index';
import { Link } from 'react-router-dom';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogContentText from '#material-ui/core/DialogContentText';
import DialogTitle from '#material-ui/core/DialogTitle';
import TextField from '#material-ui/core/TextField';
import { withSnackbar } from 'notistack';
import firebase from "firebase";
const INITIAL_STATE = {
email: '',
password: '',
error: null,
openDialog: false,
resetEmail:''
};
const byPropKey = (propertyName, value) => () => ({
[propertyName]: value,
});
class Signin extends Component{
constructor(props) {
super(props);
this.resetPassword = this.resetPassword.bind(this);
this.handleCancel = this.handleCancel.bind(this);
this.handleReset = this.handleReset.bind(this);
this.state = { ...INITIAL_STATE };
}
resetPassword(){
console.log('reset password clicked');
this.setState({
openDialog: true
})
}
handleCancel(){
this.setState({
openDialog: false
})
}
handleReset(){
const { enqueueSnackbar } = this.props;
console.log(this.state)
let auth = firebase.auth();
this.setState({
openDialog: false
})
auth.sendPasswordResetEmail(this.state.resetEmail).then(function() {
// Email sent.
enqueueSnackbar('Email sent successfully');
}).catch(function(error) {
// An error happened.
let err= error.message
enqueueSnackbar(err);
});
}
onSubmit = (event) => {
const { enqueueSnackbar } = this.props;
console.log('value of props on submit logged',this.props)
const {
email,
password,
} = this.state;
auth.doSignInWithEmailAndPassword(email, password)
.then(() => {
this.setState({ ...INITIAL_STATE });
enqueueSnackbar('sign in succesfull');
console.log(firebase.auth().currentUser);
let authUser = firebase.auth().currentUser;
console.log(authUser);
localStorage.setItem('authUser',JSON.stringify(authUser));
localStorage.setItem('isUserLoggedIn',true);
this.props.onSignIn('sign in Successfull');
this.props.history.push('/');//routing to landing page
})
.catch(error => {
this.setState(byPropKey('error', error));
console.log(error)
});
event.preventDefault();
}
render(){
const {
email,
password
} = this.state;
return(
<div className="login-body">
<div className="hybrid-login-form">
<div className="sign-in-content">
<h1>Sign In</h1>
<Form onSubmit={this.onSubmit}>
<FormGroup>
<Label className="set-label-pos" for="Email">Email</Label>
<input value={email} className="form-control" onChange={event => this.setState(byPropKey('email', event.target.value))}
type="text" placeholder="Email" />
</FormGroup>
<FormGroup>
<Label className="set-label-pos" for="Password">Password</Label>
<input className="form-control" value={password} onChange={event => this.setState(byPropKey('password', event.target.value))}
type="password" placeholder="Password" />
</FormGroup>
<Button className="btn btn-danger sign-in-btn" size="lg" type="submit">Sign In</Button>
<FormGroup check className="remember-me">
<Label className="set-label-pos" check>
<Input type="checkbox" />{' '} Remember me</Label>
<Label className="set-frgt-pass" onClick={this.resetPassword}>Forgot Password</Label>
</FormGroup>
</Form>
</div>
<div className="signup-content">
New to Faraday? <Link to="/signup">Sign up now.</Link>
</div>
</div>
<Dialog open={this.state.openDialog} onClose={this.handleClose} aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description">
<DialogTitle id="alert-dialog-title">{"Reset Password"}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Please enter your registered email address, an email will be sent for resetting password.
</DialogContentText>
</DialogContent>
<TextField
autoFocus
margin="dense"
id="name"
label="Email Address"
type="email"
onChange={event => this.setState(byPropKey('resetEmail', event.target.value))}
/>
<DialogActions>
<Button onClick={this.handleCancel} color="primary">
Cancel
</Button>
<Button onClick={this.handleReset} color="primary" autoFocus>
Reset
</Button>
</DialogActions>
</Dialog>
</div>
)
}
}
export default withSnackbar(Signin);
Header component
import React, { Component } from 'react';
import {
Collapse,
Navbar,
NavbarToggler,
Nav,
NavItem,
NavLink
} from 'reactstrap';
import './header.css';
import HeaderCollapse from './header-collapse/header-collapse';
import logo from '../../assets/logo.png';
import usericon from '../../assets/user.png';
import { ListGroup, ListGroupItem } from 'reactstrap';
import { Link } from 'react-router-dom';
class HeaderComponent extends Component {
headerList = [
{'Id':1,'Title':'K-12 Physics','submenu':['Video Lectures & Practice','White Papers','Revision Tree & Learning curve']},
{'Id':2,'Title':'Scinema','submenu':['Story of science','Modern Science','Learn Science']},
{'Id':3,'Title':'WorkShops','submenu':['Argon','Neon','Xenon']},
// {'Id':4,'Title':'Ed-Tech','submenu':['Item1','Item2','Item3']} // future sprint
];
constructor(props) {
super(props);
this.state = {
isOpen: false,
activeItem: -1,
showCollapseBar: false,
showUserAction: false,
isAuthUser:false,
selData:[]
};
this.toggle = this.toggle.bind(this);
this.hideCollapse = this.hideCollapse.bind(this);
this.showActionModel = this.showActionModel.bind(this);
this.setWrapperRef = this.setWrapperRef.bind(this);
this.handleClickOutside = this.handleClickOutside.bind(this);
this.logout = this.logout.bind(this);
this.updateState = this.updateState.bind(this);
this.onclickOutsideHeaderContainer = this.onclickOutsideHeaderContainer.bind(this);
this.handleItemClick = this.handleItemClick.bind(this);
this.collapseCallback = this.collapseCallback.bind(this);
this.setheaderContainerRef = this.setheaderContainerRef.bind(this);
console.log('header props',this.props)
}
componentDidMount() {
console.log('header component testing', localStorage.getItem('isUserLoggedIn'))
document.addEventListener('mousedown', this.handleClickOutside);
console.log('Logged in user details',localStorage.getItem('authUser'))
this.updateState();
}
updateState(){
console.log('update state triggered from header compoenet')
this.setState({isAuthUser: localStorage.getItem('isUserLoggedIn')});
console.log(localStorage.getItem('authUser'))
}
// showAdminNav(){
// console.log('show admin nav triggered')
// }
onSignIn(){
console.log('On Sign in Method call')
}
handleClickOutside(event) {
if (this.wrapperRef && !this.wrapperRef.contains(event.target)) {
this.setState({
showUserAction: !this.state.showUserAction
});
}
if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
// some code..
//alert('mobile biew')
if(event.path[0].className !== 'actionLink.nav-link'){
return;
}
//this.hideCollapse();
}
}
onclickOutsideHeaderContainer(event){
if(!this.headerContainerRef.contains(event.target))
this.hideCollapse()
}
handleItemClick(index,data) {
console.log(data)
this.setState({
activeItem: index,
showCollapseBar: true,
selData:data
})
}
toggle() {
this.setState({
isOpen: !this.state.isOpen,
//showCollapseBar: !this.state.showCollapseBar /**Commented to fix toggle */
});
this.hideCollapse();
}
hideCollapse(){
this.setState({
showCollapseBar: false
});
}
collapseCallback = (data) => {
if (data === 'hide Collapse') {
this.setState({
activeItem: -1,
showCollapseBar: false
})
}
}
showActionModel(){
this.setState({
showUserAction: !this.state.showUserAction
});
}
setWrapperRef(node) {
this.wrapperRef = node;
}
setheaderContainerRef(node){
this.headerContainerElement = node;
}
logout(){
console.log('logout method triggered')
localStorage.setItem('authUser',null);
localStorage.setItem('isUserLoggedIn',false);
this.props.history.push('/');
}
render() {
const showCollapseBar = this.state.showCollapseBar;
const showActionModel = this.state.showUserAction;
const isUserLoggedIn = this.state.isAuthUser === "true" ? true : false;
let collapseBar = "null";
if (showCollapseBar) {
collapseBar = <HeaderCollapse data = {this.state.selData} callbackFromheader={this.collapseCallback}/>;
} else {
collapseBar = null;
}
const headerList = this.headerList.map((header, index) =>
<NavItem key={header.Id.toString()} className={this.state.activeItem === index ? 'active' : ''}>
<NavLink onClick={this.handleItemClick.bind(this, index,header)}>
{header.Title}
</NavLink>
</NavItem>
);
let actionList = null;
if(isUserLoggedIn){
actionList = <ListGroup>
<ListGroupItem>Profile</ListGroupItem>
<ListGroupItem onClick={this.logout}>Logout</ListGroupItem>
</ListGroup>;
}else{
actionList = <ListGroup>
<ListGroupItem><Link className="actionLink nav-link" to="/signin">Sign In</Link></ListGroupItem>
<ListGroupItem><Link className="actionLink nav-link" to="/SignUp">Not a member yet.? Sign Up Now</Link></ListGroupItem>
</ListGroup>;
}
//<img src={logo} height="60"/>
return (
<div className="header-container" id="header-container" >
<Navbar color="light" light expand="md" dark >
<Link to="/"><img src={logo} height="60"/></Link>
<NavbarToggler onClick={this.toggle} />
<Collapse isOpen={this.state.isOpen} navbar>
<Nav className="ml-auto" navbar>
{headerList}
<NavItem>
<Link to="/contacts" onClick={this.hideCollapse} className="actionLink nav-link">Contact</Link>
</NavItem>
<NavItem>
<Link className="actionLink nav-link" to="/managedata">Admin</Link>
</NavItem>
<NavItem>
{/* <Link className="actionLink nav-link" to=""> */}
<img id="user-icon" src={usericon} onClick={this.showActionModel}/>
{/* </Link> */}
</NavItem>
</Nav>
</Collapse>
</Navbar>
{showActionModel && <div id="user-actions" ref={this.setWrapperRef}>
{actionList}
</div>}
{collapseBar}
</div>
);
}
}
export default HeaderComponent;
componentDidMount gets called only once when the component is rendered. So make sure , you call your function updateState whenever you sign in or sign out.
I think calling updateState method inside onSignIn should solve the issue if I understand the code correctly.
onSignIn() {
this.updateState()
}

React.js Form - How To Include All Values On Final Page?

So I have built a Wizard Form using React-Final-Form that I am using in my sign-up page. I am trying to figure out how I can display all user inputs on the final page as a way for the user to double-check/verify their inputs before submitting. Any help would be greatly appreciated!
(P.S. - I tried researching this before posting, but all I was able to find was storing user inputs in Redux and accessing them from there, which I'd like to avoid, if at all possible.)
Here is an example link that shows what I want to do - Please feel free to fork and play around with it if you are trying to figure out a solution! https://codesandbox.io/s/0332k02x0v
Here is the code, shortened to include only the relevant bits:
My Wizard.js page:
import React, { Component } from "react";
import PropTypes from "prop-types";
import { Form } from "react-final-form";
class Wizard extends Component {
static propTypes = {
onSubmit: PropTypes.func.isRequired
};
static Page = ({ children }) => children;
constructor(props) {
super(props);
this.state = {
page: 0,
values: props.initialValues || {}
};
}
next = values =>
this.setState(state => ({
page: Math.min(state.page + 1, this.props.children.length - 1),
values
}));
previous = () =>
this.setState(state => ({
page: Math.max(state.page - 1, 0)
}));
validate = values => {
const activePage = React.Children.toArray(this.props.children)[
this.state.page
];
return activePage.props.validate ? activePage.props.validate(values) : {};
};
handleSubmit = values => {
const { children, onSubmit } = this.props;
const { page } = this.state;
const isLastPage = page === React.Children.count(children) - 1;
if (isLastPage) {
return onSubmit(values);
} else {
this.next(values);
}
};
render() {
const { children } = this.props;
const { page, values } = this.state;
const activePage = React.Children.toArray(children)[page];
const isLastPage = page === React.Children.count(children) - 1;
return (
<Form
initialValues={values}
validate={this.validate}
onSubmit={this.handleSubmit}
>
{({ handleSubmit, submitting, values }) => (
<form onSubmit={handleSubmit}>
{activePage}
<div className="buttons">
{page > 0 && (
<button type="button" onClick={this.previous}>
« Previous
</button>
)}
{!isLastPage && <button type="submit">Next »</button>}
{isLastPage && (
<button type="submit" disabled={submitting}>
Submit
</button>
)}
</div>
{/* <pre>{JSON.stringify(values, 0, 2)}</pre> */}
</form>
)}
</Form>
);
}
}
export default Wizard;
My index.js page:
import React, { Component } from "react";
import { Field } from "react-final-form";
import formatString from "format-string-by-pattern";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import Wizard from "./Wizard";
import Styles from "./Styles";
import { addUser } from "../../../actions/authActions";
class ReactFinalForm2 extends Component {
state = {};
render() {
const onSubmit = async values => {
this.props.addUser(values);
// API query here
};
const Error = ({ name }) => (
// Error handing here
);
return (
<Styles>
<div>
<Wizard initialValues={{}} onSubmit={onSubmit}>
<Wizard.Page
validate={values => {
// Page validation here
}}
>
// Page inputs here
</Wizard.Page>
<Wizard.Page
validate={values => {
// Page validation here
}}
>
// Page inputs here
</Wizard.Page>
<Wizard.Page
validate={values => {
// Page validation here
}}
>
// Page inputs here
</Wizard.Page>
<Wizard.Page>
{/* *** THIS IS WHERE I WOULD LIKE TO DISPLAY ALL PREVIOUS VALUES (SO THE USER CAN CONFIRM / DOUBLE-CHECK THEIR INPUTS BEFORE SUBMITTING) *** */}
</Wizard.Page>
</Wizard>
</div>
</Styles>
);
}
}
ReactFinalForm2.propTypes = {
addUser: PropTypes.func.isRequired
};
export default connect(
null,
{ addUser }
)(ReactFinalForm2);
I have added a state to the parent component. Changing this state on every submit from the child. I have JSON stringify the state in parent component. As you said no need to use redux, this is the workaround I came with. Still your code has a potential for improvements. Please check this working sandbox:
[ https://codesandbox.io/s/zrvloq4o6x ]
Wizard.js change
handleSubmit = values => {
const { children, onSubmit } = this.props;
const { page } = this.state;
const isLastPage = page === React.Children.count(children) - 1;
if (isLastPage) {
return onSubmit(values);
} else {
this.next(values);
}
// Change added
this.props.onStateChange(values);
};
Index.js
import React from "react";
import { render } from "react-dom";
import Styles from "./Styles";
import { Field } from "react-final-form";
import Wizard from "./Wizard";
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const onSubmit = async values => {
await sleep(300);
window.alert(JSON.stringify(values, 0, 2));
};
const Error = ({ name }) => (
<Field
name={name}
subscribe={{ touched: true, error: true }}
render={({ meta: { touched, error } }) =>
touched && error ? <span>{error}</span> : null
}
/>
);
const required = value => (value ? undefined : "Required");
let data = {};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.onStateChange = this.onStateChange.bind(this);
}
onStateChange = values => {
this.setState({ data: values });
console.log(values);
};
render() {
return (
<Styles>
<h1>🏁 React Final Form Example</h1>
<h2>Wizard Form</h2>
<a href="https://github.com/erikras/react-final-form#-react-final-form">
Read Docs
</a>
<p>
Notice the mixture of field-level and record-level (or{" "}
<em>page-level</em> in this case) validation.
</p>
<Wizard
initialValues={{}}
onStateChange={this.onStateChange}
onSubmit={onSubmit}
>
<Wizard.Page>
<div>
<label>First Name</label>
<Field
name="firstName"
component="input"
type="text"
placeholder="First Name"
validate={required}
/>
<Error name="firstName" />
</div>
<div>
<label>Last Name</label>
<Field
name="lastName"
component="input"
type="text"
placeholder="Last Name"
validate={required}
/>
<Error name="lastName" />
</div>
</Wizard.Page>
<Wizard.Page
validate={values => {
const errors = {};
if (!values.notes) {
errors.notes = "Required";
}
return errors;
}}
>
<div>
<label>Best Stooge?</label>
<div>
<label>
<Field
name="stooge"
component="input"
type="radio"
value="larry"
/>{" "}
Larry
</label>
<label>
<Field
name="stooge"
component="input"
type="radio"
value="moe"
/>{" "}
Moe
</label>
<label>
<Field
name="stooge"
component="input"
type="radio"
value="curly"
/>{" "}
Curly
</label>
</div>
</div>
<div>
<label>Notes</label>
<Field name="notes" component="textarea" placeholder="Notes" />
<Error name="notes" />
</div>
</Wizard.Page>
<Wizard.Page>
<div>
<p>
<b>Display all previous values here for user verification </b>
<br />
<i>{JSON.stringify(this.state.data, 0, 2)}</i>
</p>
</div>
</Wizard.Page>
</Wizard>
</Styles>
);
}
}
render(<App />, document.getElementById("root"));

How to bind the elements to the modal in React?

I am using rendering elements from dynamodb using serverless and showing in a card dashboard and when i am editing the card i want to see the contents that are already present in the card .
Edit.js
import React, { Component } from "react";
import { Form, Modal, Button, Container, Icon } from "semantic-ui-react";
import Amplify, { API } from "aws-amplify";
const uuidv1 = require("uuid/v1");
class EditItemModal extends Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.deleteItem = this.deleteItem.bind(this);
this.state = { item: this.props.item };
}
deleteItem() {
let apiName = "ServerlessReactExampleCRUD";
let path = "/ServerlessReactExample/object/" + this.props.item[0].ID;
API.del(apiName, path)
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error.response);
});
this.props.getItems();
this.handleClose();
}
handleChange(event, { name, value }) {
this.setState({ [name]: value });
}
handleSubmit(event) {
let apiName = "ServerlessReactExampleCRUD";
let path = "/ServerlessReactExample";
let editItem = {
body: {
ID: this.props.item[0].ID,
name: this.state.name,
price: this.state.price,
description: this.state.description
}
};
API.put(apiName, path, editItem)
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error.response);
});
this.props.getItems();
this.handleClose();
event.preventDefault();
}
handleOpen = () => this.setState({ modalOpen: true });
handleClose = () => this.setState({ modalOpen: false });
render() {
return (
<Container style={{ padding: 10 }}>
<Modal
trigger={
<Button icon onClick={this.handleOpen}>
<Icon name="edit" />
</Button>
}
open={this.state.modalOpen}
closeIcon
onClose={this.handleClose}
>
<Modal.Header>Edit</Modal.Header>
<Modal.Content>
<Form onSubmit={this.handleSubmit}>
<Form.Group unstackable widths={2}>
<Form.Input
name="name"
label="Item Name"
placeholder="Edit Item Name..."
onChange={this.handleChange}
value={this.state.name}
/>
<Form.Input
name="price"
label="Item Price"
placeholder="£0.00"
onChange={this.handleChange}
value={this.state.price}
/>
</Form.Group>
<Form.TextArea
name="description"
label="Item Description"
placeholder="Edit Description of the Item..."
onChange={this.handleChange}
value={this.state.description}
/>
<Form.Button type="submit">Submit</Form.Button>
</Form>
</Modal.Content>
<Modal.Actions>
<Button icon labelPosition="left" onClick={this.deleteItem}>
<Icon name="delete" />
Delete Item
</Button>
</Modal.Actions>
</Modal>
</Container>
);
}
}
export default EditItemModal;
This is how the card looks like
When i clicked on edit button it will look like this
What i want to show the values that are present in the card ? I want to know how i can bind them to show in the card ?
Any help is appreciated.
Ref : Ref links
As Simão wrote you're probably accessing wrong element
this.state = { item: this.props.item };
creates this.state.item (with your values/properties inside ?)
If you don't want to change everything probably this will fix it:
this.state = { ...this.props.item };
I would move operations into parent to keep all (CRUD) in one place. Updating/deleting item on list in handlers could avoid unnecessary list reloading (getItems) and done before api call would work as optimistic update.

Categories

Resources