I have to implement a form with a bit less than 30 different fields.
So I decided to split them in 2 differents container component with two tabs to navigate between them.
I use redux-form to handle the data binding.
For on component I can get the value from handleSubimit of one component. But the final validation must be in the last tab only. From here I only have access to the value of the second tab. Like the data from the store where wipe out.
How can I access the store where my previous data should be ?
TabNavigationBar.js
import React from 'react';
import TabNavigationItem from './TabNavigationItem';
const TabNavigationBar = ({ onTabChange, activeTab }) => {
const tabList = [
{ hasIcon: 'fas fa-user-circle', hastext: 'Information Utilisateur' },
{ hasIcon: 'fas fa-file-alt', hastext: 'Informations contrat' }
];
const clickOnTab = tabNumer => {
onTabChange(tabNumer);
};
return (
<div className="columns">
<div className="column is-offset-one-quarter-desktop is-offset-one-thirds-tablet is-half-desktop is-one-thirds-tablet">
<div className="tabs is-toggle is-fullwidth">
<ul>
{tabList.map((tab, i) => (
<TabNavigationItem
key={i}
tabSelected={() => clickOnTab(i)}
hasClass={activeTab === i ? 'is-active' : ''}
hasIcon={tab.hasIcon}
hasText={tab.hastext}
/>
))}
</ul>
</div>
</div>
</div>
);
};
export default TabNavigationBar;
UserForm.js
import React from 'react';
import { reduxForm } from 'redux-form';
import CiviliteRadioButton from './CiviliteRadioButton';
import NameInputs from './NameInputs';
import AddressInputs from './AddressInputs';
import MailAndDOB from './MailAndDOB';
import TelephoneInputs from './TelephoneInputs';
let UserForm = ({ handleSubmit }) => {
return (
<div>
<CiviliteRadioButton />
<NameInputs />
<AddressInputs />
<MailAndDOB />
<TelephoneInputs />
</div>
);
};
UserForm = reduxForm({
form: 'form1',
initialValues: {
user: {
adresse: {
country: 'France'
},
civilite: 'Madame'
}
}
})(UserForm);
export default UserForm;
ContractForm.js
import React from 'react';
import { reduxForm } from 'redux-form';
import InputItem from '../InputItem';
import ContratInputsList from './contratInputList';
let ContratForm = ({ handleSubmit }) => {
const submit = values => {
console.log(values);
};
return (
<div>
<div className="columns is-multiline ">
{ContratInputsList.map((item, i) => {
return (
<div className="column is-half" key={i}>
<InputItem spec={item.spec} />
</div>
);
})}
</div>
<div className="columns">
<div className="column">
<div className="field is-grouped is-grouped-right">
<input
className="button is-primary"
onClick={handleSubmit(submit)}
type="submit"
value="Envoyer"
/>
</div>
</div>
</div>
</div>
);
};
ContratForm = reduxForm({
form: 'form2'
})(ContratForm);
export default ContratForm;
EDIT
When I click on my tabs, redux-form/DESTROY is called and erase form1's data.
Try setting destroyOnUnmount flag to false in reduxForm(options).
Related
I am new to REACT and the MERN Stack and try to understand everything. But sometimes, it seems that the simplest things do not want to get into my head.
I hope that, one day, I will understand it all. Until then it still seems a long way away. Anyway. I am starting with a simple MERN app. All users should be displayed on the start page. On a separate "page" there should be a create users form. For now, the users are displayed on the home screen but when I switch back from the "create users page" they dissappeared. Furthermore the input form do not work (validation error). When checking get and posts requests from my backend with Thunder Client everthing works, so I suppose, this might be something to do with the frontend. Sorry for my wording. I am a programming newbie.
I hope it is somewhat understandable. What am I doing wrong? I would be so happy, if anyone could help. Thank you!
client/src/App.js
import React from "react";
import { Routes, Route } from "react-router-dom";
import AllUsers from "./pages/AllUsers";
import Navigation from "./components/Navigation";
import CreateUser from "./pages/CreateUser";
import "./App.css";
function App() {
return (
<div className="App">
<Navigation />
<Routes>
<Route path="/" element={<AllUsers />} />
<Route path="create-User" element={<CreateUser />} />
</Routes>
</div>
);
}
export default App;
client/src/components/Navigation.js
import React from "react";
import { Link } from "react-router-dom";
const Navigation = () => {
return (
<header className="bg-background border-t-0 shadow-none">
<nav className="bg-navigation bg-opacity-40 rounded-t-xs flex justify-around h-12 p-3 ">
<Link to="/create-user">Create User</Link>
<Link to="/">
<img id="workshop-icon" src="../assets/home.svg" alt="home button" />
</Link>
</nav>
</header>
);
};
export default Navigation;
client/src/AllUsers.js
import React from "react";
import { useState, useEffect } from "react";
import Axios from "axios";
const AllUsers = () => {
const [listOfUsers, setListOfUsers] = useState([]);
useEffect(() => {
Axios.get("http://localhost:3001/getUsers").then((response) => {
setListOfUsers(response.data);
});
}, []);
return (
<div className="App">
<div className="usersDisplay">
{listOfUsers.map((user) => {
return (
<div key={user._id}>
<h1>Name: {user.name}</h1>
<h1>Age: {user.age}</h1>
<h1>Username: {user.username}</h1>
</div>
);
})}
</div>
</div>
);
};
export default AllUsers;
client/src/createUser.js
import React from "react";
import { useState } from "react";
import Axios from "axios";
const CreateUser = () => {
const [listOfUsers, setListOfUsers] = useState([]);
const [name, setName] = useState("");
const [age, setAge] = useState(0);
const [username, setUsername] = useState("");
Axios.post("http://localhost:3001/createUser", {
name,
age,
username,
}).then((response) => {
setListOfUsers([
...listOfUsers,
{
name,
age,
username,
},
]);
});
return (
<div className="input">
<div>
<input
type="text"
placeholder="Name..."
onChange={(event) => {
setName(event.target.value);
}}
/>
<input
type="number"
placeholder="Age..."
onChange={(event) => {
setAge(event.target.value);
}}
/>
<input
type="text"
placeholder="Username..."
onChange={(event) => {
setUsername(event.target.value);
}}
/>
<button onClick={CreateUser}> Create User </button>
</div>
</div>
);
};
export default CreateUser;
I am trying to implement tailwindcss styles into Formik to style forms. But the styles declared through className are not being applied?
I think Formik is using classname to define input type and tailwind uses the same method to declare styles?
my form page:
import React from "react";
import * as yup from "yup"
import { Formik, Form } from 'formik'
import { MyTextInput } from "../components/FormParts";
let mainFormSchema = yup.object().shape({
name: yup.string().trim().required()
})
const tryMainForm = () => {
const initValues = {
name: ''
}
return (
<div>
<h1>Any place in your app!</h1>
<Formik
initialValues={initValues}
validationSchema={mainFormSchema}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 400);
}}
>
{({ isSubmitting }) => (
<Form>
<MyTextInput
label="name"
name="name"
type="text"
placeholder="name"
/>
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</Form>
)}
</Formik>
</div>
)
}
export default tryMainForm
formpart component:
import { useField } from "formik";
export const MyTextInput = ({ label, ...props }) => {
const [field, meta] = useField(props)
return (
<>
<label htmlFor={props.id || props.name} className="block text-sm font-medium text-gray-700">{label}</label>
<input className="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md" {...field} {...props} />
{meta.touched && meta.error ? (
<div className="error">{meta.error}</div>
) : null}
</>
)
}
can you suggest a way to deal with this?
tailwind applies styles properly to label but doesn't with input?
I am new to Vue and I am trying to validate a form made by multiple select rendered by a vfor. The date is coming from a Json, simulated mock-server-json.
I can use Vue Vanilla or vee-validate. I saw I could use useFieldArray with vee-validate but I could not make it work.
<template>
<ux-loader v-if="dataArray.length == 0" loading></ux-loader>
<transition name="onEnter">
<div v-if="dataArray.length != 0">
<form #submit.prevent="handleSubmit">
<div class="form">
<div v-for="(data, index) in dataArray" :key="index" class="select">
{{ index }}
<ux-input-a11y-select v-model="form.selected[index]">
<option data-placeholder value="">-- Choisir une valeur --</option>
<option v-for="option in data.option" :key="option" :value="option">{{ option }}</option>
</ux-input-a11y-select>
</div>
</div>
<button class="submit">Valider</button>
</form>
<Modal v-show="isModalVisible" #close="closeModal" />
</div>
</transition>
<div v-if="error != null">{{ error }}</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import Modal from '../components/Modal.vue'
import { UxBtn, UxInputA11ySelect, UxLoader } from '#libui/ux-default-lib'
import getForm from '../composables/getForm.js'
import postForm from '../composables/postForm.js'
UxInputA11ySelect.define()
UxBtn.define()
UxLoader.define()
export default defineComponent({
name: 'FormUser',
components: {
Modal,
},
setup() {
const dataArray = ref([])
const { error, load } = getForm()
const { sendData } = postForm()
load().then((result: any) => {
dataArray.value = result
})
return { sendData, error, dataArray }
},
data() {
return {
isModalVisible: false,
form: {
selected: [],
},
}
},
methods: {
handleSubmit() {
this.isModalVisible = true
},
async closeModal() {
this.isModalVisible = false
console.log(this.form.selected)
console.log(Object.values(this.form.selected))
this.sendData(this.form.selected)
this.$router.push('Display')
},
},
})
</script>
This is my current code. It is working as I can get an object containing the results of the selects but I am wondering if there is a better way of doing it, like in Angular where you get an object with all results without doing anything particular.
Any help appreciated.
I can't seem to be able to login to my app that I'm making when I click on my "Log in with Facebook" button. Also, I'm using React JS. I think it might have something to do with not having REACT_APP_FACEBOOKLOGIN=######### (where ######### is my Facebook Developer ID) inside of a .env file.
import React, { Component } from 'react';
import logo from './logo.png';
import FacebookLogin from 'react-facebook-login';
import Sidebar from "react-sidebar";
import LeaveGroup from "./components/LeaveGroup";
// import firebase from "./utils/firebase.js";
import { GoogleApiWrapper } from 'google-maps-react';
import MapBox from "./components/MapBox";
// import ReactDOM from 'react-dom'
import ChatBox from "./components/ChatBox";
import API from "./utils/API";
import './App.css';
import CreateGroup from './components/CreateGroup/CreateGroup';
require("dotenv").config();
class App extends Component {
constructor(props) {
super(props);
this.state = {
sidebarOpen: true,
name: "",
id: "",
chatText: "",
messagesArray: [],
groupName: "",
};
this.onSetSidebarOpen = this.onSetSidebarOpen.bind(this);
}
onSetSidebarOpen(open) {
this.setState({ sidebarOpen: open });
};
componentDidMount() {
this.loadUsers();
}
loadUsers = () => {
API.getUsers()
.then(res =>
this.setState({ users: res.data }, () => console.log("loadUsers Data: ", res.data))
)
.catch(err => console.log(err));
};
saveUsers = (data) => {
API.saveUser(data)
.then(res =>
console.log(res))
}
handleOnClick = event => {
event.preventDefault();
if (this.state.name) {
API.saveUser({
name: this.state.name,
})
.then(res => this.loadUsers())
.catch(err => console.log(err));
}
};
responseFacebook = (response) => {
console.log(response);
this.setState({ name: response.name, id: response.id })
this.saveUsers(response);
}
render() {
return (
<div className="App">
<Sidebar
sidebar={<b>
<button onClick={() => this.onSetSidebarOpen(false)}>
×
</button>
<div>
<img id="logo-image" src={logo} alt="catchup-app-logo" />
</div>
<div className="about-text">
{/* <p>The CatchUp! app allows you to
create, share and join private location based groups.
</p> */}
</div>
<div
style={{ padding: 40 }}>
<br />
<FacebookLogin
appId={process.env.REACT_APP_FACEBOOKLOGIN}
autoLoad={true}
reauthenticate={true}
fields="name,email,picture"
callback={this.responseFacebook}
/>
<br />
<br />
</div>
{/* Create group box */}
<CreateGroup
groupName={this.state.groupName}
groupMember={this.state.name}
/>
{/* Join group box */}
<div className="text-box">
<p>
<button class="btn btn-dark" type="button" data-toggle="collapse" data-target="#collapseExample1" aria-expanded="false" aria-controls="collapseExample">
Join a Group
</button>
</p>
<div class="collapse" id="collapseExample1">
<div class="card card-body">
<div class="input-group mb-3">
<input type="text" class="form-control" placeholder="Group Name" aria-label="Group Name" aria-describedby="button-addon2" />
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="button" id="button-addon">Join</button>
</div>
</div>
</div>
</div>
</div>
<ChatBox
name={this.state.name}
groupName={this.state.groupName}
messagesArray={this.state.messagesArray}
/>
</b>}
open={this.state.sidebarOpen}
onSetOpen={this.onSetSidebarOpen}
styles={{ sidebar: { background: "white" } }}
>
<button onClick={() => this.onSetSidebarOpen(true)}>
<i class="fas fa-bars"></i>
</button>
</Sidebar>
<br />
<br />
<MapBox
gProps={this.props.google}
gZoom={17}
gOnMarkerClick={this.gOnMarkerClick}
gName={this.state.name}
gGroupName={this.state.groupName}
gOnClose={this.onInfoWindowClose}
/>
</div>
);
}
}
export default GoogleApiWrapper({
apiKey: `${process.env.REACT_APP_GOOGLE_MAPS_API_KEY}`
})(App)
I want this to be able to get this to work so move onto logging in a user into my MongoDB.
I'm working a react form using the controlled inputs to access the change on the form inputs. It contained lot of fields but i haven't posted all the fields.
import React from 'react';
import Links from './Links.jsx';
import axios from 'axios';
import Dialog from 'react-bootstrap-dialog';
import {Typeahead} from 'react-bootstrap-typeahead';
import Autocomplete from 'react-autocomplete';
import style from './app.css';
class AddNewEmployee extends React.Component {
constructor(props){
super(props);
this.state = {
firstName : '',
middleName : ''
}
this.handleUserInput = this.handleUserInput.bind(this);
}
handleUserInput(){
const name = e.target.name;
const value = e.target.value;
this.setState({
[name]: value
})
}
render() {
return(
<div className = "col-sm-6">
<input type="text"
placeholder="Enter First Name" name="firstName"
value={this.state.firstName} onChange={(event) => this.handleUserInput(event)}>
</input>
</div>
<div className = "col-sm-6">
<input type="text"
placeholder="Enter First Name" name="middleName"
value={this.state.middleName} onChange={(event) => this.handleUserInput(event)}>
</input>
</div>
)
}
}
export default AddNewEmployee;
The above code is working fine so far, but i got a requirement to keep an nested object to save the form input values.
But i'm missing some thing here how to handle the user input on form elements.
How shall we handle the user input when we have a nested object.
import React from 'react';
import Links from './Links.jsx';
import axios from 'axios';
import Dialog from 'react-bootstrap-dialog';
import {Typeahead} from 'react-bootstrap-typeahead';
import Autocomplete from 'react-autocomplete';
import style from './app.css';
class AddNewEmployee extends React.Component {
constructor(props){
super(props);
this.state = {
employee : {
firstName : '',
middleName : ''
}
}
this.handleUserInput = this.handleUserInput.bind(this);
}
handleUserInput(){
// here how to handle those input changes
}
render() {
return(
<div className = "col-sm-6">
<input type="text"
placeholder="Enter First Name" name="firstName"
value={this.state.employee.firstName} onChange={(event) => this.handleUserInput(event)}>
</input>
</div>
<div className = "col-sm-6">
<input type="text"
placeholder="Enter First Name" name="middleName"
value={this.state.employee.middleName} onChange={(event) => this.handleUserInput(event)}>
</input>
</div>
)
}
}
export default AddNewEmployee;
handleUserInput(e) {
// Option #1 (mutable data)
let {employee} = this.state;
const name = e.target.name;
const value = e.target.value;
employee[name] = value;
this.setState({
employee
});
// Option #2 (immutable data)
const { employee } = this.state;
const name = e.target.name;
const value = e.target.value;
this.setState({
employee: {
...employee,
[name] : value
}
});
}
Also, since you are binding handleUserInput in the constructor, you can simplify onChange={(event) => this.handleUserInput(event)} to onChange={this.handleUserInput}