A strategy to alter redux state with action in useEffect()? - reactjs

I'm trying to implement a component that takes a phone number from the redux store, parses the number up on component load with useEffect(), puts the parsed elements (country code and ten digit number) into their own state with useState() and then on onChange of the phone form, update both sections of the number AND update the full phone prop coming from the redux store. I'm obviously not doing something right here:
import React, { useState, useEffect } from 'react';
import { object, func, bool } from 'prop-types';
import { Row, Col } from 'styled-bootstrap-grid';
import { connect } from 'react-redux';
import './BillingDetails.css';
import Button from 'components/Button';
import {
setFirstName,
setLastName,
setPhone,
setEmail,
} from '../../../store/signup/actions';
import {
billingDetailsError,
isValidInput,
regExpValidations,
} from '../validateInput';
import InnerTitle from './InnerTitle';
import Input from './Input';
import PhoneInput from './PhoneInput';
import CountryCodeInput from './CountryCodeInput';
import Dropdown from './Dropdown';
const BillingDetails = ({ signup, onChange, onBlur, onNext, inputError }) => {
const { first_name: firstName, last_name: lastName, email, phone } = signup;
const [countryCode, setCountryCode] = useState('');
const [mobileNumber, setMobileNumber] = useState('');
// Parse the incoming phone prop into country code and the ten digit mobile phone. Sets separate state for both.
useEffect(() => {
const phoneArray = phone.split('');
const cc = phoneArray.splice(0, phoneArray.length - 10).join('');
setCountryCode(cc);
}, '');
useEffect(() => {
const phoneArray = phone.split('');
const tenDigitNumber = phoneArray.join('');
setMobileNumber(tenDigitNumber);
}, '');
useEffect(() => {
interpolatePhone();
}, [countryCode, mobileNumber]);
const interpolatePhone = () => {
const fixedMobile = mobileNumber.replace(/[- )(]/g, '');
const interpolatedPhone = `${countryCode}${fixedMobile}`;
setPhone(interpolatedPhone);
};
// Onsubmit concatenates country code and mobile number, calls setPhone action with concat number and onNext().
const onSubmit = e => {
e.preventDefault();
setPhone(interpolatePhone());
onNext();
};
skipping to relevant JSX:
<Col sm="12" md="12">
<>
<div className="phone-input">
<Col sm="3" md="4">
<CountryCodeInput
label="mobile number"
name="country_code"
onChange={e => setCountryCode(e.target.value)}
onBlur={onBlur}
type="country_code"
value={countryCode}
inputValue={!countryCode ? '+1' : countryCode}
masked
error={
inputError && !isValidInput(phone, regExpValidations.phone)
}
/>
</Col>
<Col sm="1" md="1">
|
</Col>
<Col sm="3" md="4">
<PhoneInput
autoFocus
name="mobile_phone"
onChange={e => setMobileNumber(e.target.value)}
onBlur={onBlur}
type="phone"
value={mobileNumber}
inputValue={mobileNumber}
masked
error={
inputError && !isValidInput(phone, regExpValidations.phone)
}
/>
</Col>
</div>
</>
</Col>
<Button
className="full-width"
onClick={onSubmit}
disabled={billingDetailsError(signup)}
>
Continue
</Button>
</Button>
</Row>
</Col>
);
};
const mapStateToProps = state => ({
signup: state.signup,
});
export default connect(mapStateToProps)(BillingDetails);

Related

passing a variable from child component to parent component in Next.js

I have 2 components home and tiny tiny is imported inside home as u can see in the code
I am trying to pass value.toString("html") from tiny.js to home.js
if this is not possible at least help me integrate both tiny and home components as a single object so that I don't have to pass the value as props to a parent component
import React from "react";
import Tiny from "./tiny";
function Home({ data }) {
const [Questions, setQuestions] = useState();
const [deatils1, setdeatils] = useState();
function clickQuestion() {
axios
.post("https://askover.wixten.com/questionpost", {
Name: Questions,
Summary: deatils1,//pass tiny value as summery
})
.then(() => {
window.location.reload();
});
}
function question(e) {
setQuestions(e.target.value);
}
return (
<>
<div>
<div className="container search-box">
<Form>
<Form.Group className="mb-3" controlId="exampleForm.ControlInput1">
<Form.Label>Title</Form.Label>
<Form.Control
type="text"
onChange={question}
placeholder="ask anything?"
/>
</Form.Group>
<Tiny /> //tiny component
</Form>
<Button
type="submit"
disabled={!deatils1 || !Questions}
onClick={clickQuestion}
variant="outline-secondary"
id="button-addon2"
>
ask?
</Button>
</div>
</div>
</>
);
}
tiny.js
import React, { useState, useEffect } from "react";
import dynamic from "next/dynamic";
import PropTypes from "prop-types";
//import the component
const RichTextEditor = dynamic(() => import("react-rte"), { ssr: false });
const MyStatefulEditor = ({ onChange }) => {
const [value, setValue] = useState([]);
console.log(value.toString("html"));
useEffect(() => {
const importModule = async () => {
//import module on the client-side to get `createEmptyValue` instead of a component
const module = await import("react-rte");
console.log(module);
setValue(module.createEmptyValue());
};
importModule();
}, []);
const handleOnChange = (value) => {
setValue(value);
if (onChange) {
onChange(value.toString("html"));
}
};
return <RichTextEditor value={value} onChange={handleOnChange} />;
};
MyStatefulEditor.propTypes = {
onChange: PropTypes.func,
};
export default MyStatefulEditor;
Actually, you already have onChange event in tiny, so you only need to pass another onChange event from home to tiny.
import React from "react";
import Tiny from "./tiny";
function Home({ data }) {
const [Questions, setQuestions] = useState();
const [details, setDetails] = useState();
function clickQuestion() {
axios
.post("https://askover.wixten.com/questionpost", {
Name: Questions,
Summary: details,//pass tiny value as summery
})
.then(() => {
window.location.reload();
});
}
function question(e) {
setQuestions(e.target.value);
}
return (
<>
<div>
<div className="container search-box">
<Form>
<Form.Group className="mb-3" controlId="exampleForm.ControlInput1">
<Form.Label>Title</Form.Label>
<Form.Control
type="text"
onChange={question}
placeholder="ask anything?"
/>
</Form.Group>
<Tiny onChange={(value) => setDetails(value)}/> //tiny component
</Form>
<Button
type="submit"
disabled={!deatils1 || !Questions}
onClick={clickQuestion}
variant="outline-secondary"
id="button-addon2"
>
ask?
</Button>
</div>
</div>
</>
);
}

MUI TextField breaks Validation Schema when addind Onchange

this is my first thread I hope i do this right,
I am trying to send data for mysql server and it goes well, however when I use TextField from MUI the validation schema starts looking funny and gives error ("require field") even with the text there, However if i submit the data the mysql receives the data.
and here goes the code...
//uses Express send data to mysql
import { useState, useEffect } from 'react'
import axios from "axios"
import { Container, Grid, Typography} from '#material-ui/core';
import { makeStyles } from '#material-ui/core/styles';
import { Formik, Form } from 'formik';
import * as Yup from 'yup';
import Textfield from './Forms/IndividualComponents/TextField';
import Button from './Forms/IndividualComponents/Button'
const useStyles = makeStyles((theme) => ({
formWrapper: {
marginTop: theme.spacing(10),
marginBottom: theme.spacing(8),
},
}));
const INITIAL_FORM_STATE = {
firstName: ''
};
const FORM_VALIDATION = Yup.object().shape({
firstName: Yup.string("enter your first name")
.required('Required')
});
export default function ImagesUpload() {
const [firstName, setFirstName] = useState()
const [submitform, setSubmitForm] = useState([])
useEffect(() => {
(async() => {
const result = await axios.get('/submitform')
setSubmitForm(result.data.submitform)
})()
}, [])
const submit = async event => {
event.preventDefault()
const data = new FormData()
data.append('firstName', firstName)
const result = await axios.post('/submitForm', data)
setSubmitForm([result.data, ...submitform])
console.log(firstName)
}
const classes = useStyles();
return (
<Grid item xs={12}>
<Container maxWidth="md">
<div className={classes.formWrapper}>
<Formik
initialValues={{
...INITIAL_FORM_STATE
}}
validationSchema={FORM_VALIDATION}
>
<Form onSubmit={submit}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography>
Personal details
</Typography>
</Grid>
<Grid item xs={6}>
<Textfield
name="firstName"
label="First Name"
value={firstName}
onChange={(event => setFirstName(event.target.value) )}
type="text"
/>
</Grid>
<button type="submit">Submit</button>
</Grid>
</Form>
</Formik>
<main>
{submitform.map(post => (
<figure key={post.id}>
<figcaption>{post.firstName}</figcaption>
</figure>
))}
</main>
</div>
</Container>
</Grid>
);
}
the Textfield im using is a costum one with the following code:
import React from 'react';
import { TextField } from '#material-ui/core';
import { useField } from 'formik';
const TextfieldWrapper = ({
name,
...otherProps
}) => {
const [field, mata] = useField(name);
const configTextfield = {
...field,
...otherProps,
fullWidth: true,
};
if (mata && mata.touched && mata.error) {
configTextfield.error = true;
configTextfield.helperText = mata.error;
}
return (
<TextField {...configTextfield} />
);
};
export default TextfieldWrapper;
the problem here is the onChange: If i don't use it I cant setFirstName to send the data, but when I use it it breaks the validation... I tried everything i can find online but this is taking away my sleep for real ! :)

Get method is being called infinitely

I am fetching the data to populate it on the form but when I try to edit the data in the input, the input value will return to its original value and it is because of the get method that is infinitely render on the component. I really need your eyes to see something that have missed or missed up. Thanks in advance y'all.
fetch method
import * as api from '../api/profile';
export const getProfile = () => async (dispatch) => {
try {
const { data } = await api.fetchProfile();
dispatch({ type: 'FETCH_ALL', payload: data });
} catch (error) {
console.log(error.message);
}
}
Profile container
import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { getProfile } from '../../../actions/profile'; //fetch method
import Profile from './Profile';
function Index() {
const dispatch = useDispatch();
const posts = useSelector((state) => state.posts);
const currentId = useState(null);
useEffect(() => {
dispatch(getProfile());
}, [currentId, dispatch]);
return (
<div className="custom-container">
{posts.map((profile) => (
<div key={profile._id}>
<Profile profile={profile} currentId={currentId} />
</div>
))}
</div>
);
}
export default Index;
Profile form component
import './Profile.css';
import { React, useState, useEffect } from 'react';
import Button from 'react-bootstrap/Button';
import { TextField } from '#material-ui/core';
import { useDispatch, useSelector } from 'react-redux';
import { updateProfile } from '../../../actions/profile';
const Profile = ({ profile, currentId }) => {
const dispatch = useDispatch();
currentId = profile._id;
const [postData, setPostData] = useState(
{
profile: {
name: "",
description: "",
email: "",
number: "",
}
}
);
const post = useSelector((state) => currentId ? state.posts.find((p) => p._id === currentId) : null);
useEffect(() => {
if(post) setPostData(post);
}, [post])
const handleSubmit = (e) => {
e.preventDefault();
if(currentId) {
dispatch(updateProfile(currentId, postData));
}
}
return (
<form autoComplete="off" noValidate className="form" onSubmit={handleSubmit}>
<TextField
id="name"
name="name"
className="name"
label="Full Name"
variant="outlined"
value={postData.profile.name}
onChange={(e) => setPostData({...postData, profile: {...postData.profile, name: e.target.value}})}
/>
<TextField
id="outlined-multiline-static"
label="Multiline"
multiline
rows={4}
variant="outlined"
size="small"
className="mb-3"
name="description"
value={postData.profile.description}
onChange={(e) => setPostData({...postData, profile: {...postData.profile, description: e.target.value}})}
fullWidth
/>
<TextField
id="email"
label="Email"
variant="outlined"
size="small"
className="mb-3"
name="email"
value={postData.profile.email}
onChange={(e) => setPostData({...postData, profile: {...postData.profile, email: e.target.value}})}
/>
<TextField
id="phone"
label="Phone Number"
variant="outlined"
size="small"
name="phone"
value={postData.profile.number}
onChange={(e) => setPostData({...postData, profile: {...postData.profile, number: e.target.value}})}
/>
<Button variant="light" type="submit" className="Save">Save</Button>
</form>
);
}
export default Profile;
In useEffect you have passed like this , in Profile Container
useEffect(() => {
dispatch(getProfile());
}, [currentId, dispatch]);
in params you have passed dispatch also , so it will call dispatch every time dispatch runs , so it is called infinite times , remove it
It might be because of useEffect.
useEffect(() => {
if(post) setPostData(post);
}, [post])
The post will be a different object each time. check for post property in useEffect dependency like:
useEffect(() => {
if(post) setPostData(post);
}, [post.profile.description])
Also, why are you changing prop value below in Profile component?
currentId = profile._id

reset React select when options change

How to reset a react-select when the options is changed, this happen because im using chaining select, so my second select options will change based on my first select, what im trying to do is reset back the select to "please select" when my second option already picked before, im using react-select with react-hook-form
import React, { useState, useEffect } from 'react';
import { default as ReactSelect } from 'react-select';
import { FormGroup, Label } from 'reactstrap';
import { useFormContext, Controller } from 'react-hook-form';
import { ErrorMessage } from '#hookform/error-message';
export default function Select(props) {
const {
label,
isMulti,
note,
// isDisabled,
// withDefaultValue,
options,
isClearable,
name,
placeholder = 'Pilihan'
} = props;
const rhfContext = useFormContext(); // retrieve all hook methods
const { control, errors } = rhfContext || {};
const [elOptions, setElOptions] = useState([]);
useEffect(() => {
setElOptions(options);
}, [options]);
return (
<FormGroup>
{label && <Label htmlFor={name || ''}>{label}</Label>}
<Controller
as={ReactSelect}
name={name}
control={control}
options={elOptions}
placeholder={placeholder}
styles={customStyles}
{...(isMulti ? { isMulti: true } : {})}
{...(isClearable ? { isClearable: true } : {})}
classNamePrefix="react-select-pw"
className="react-select-container"
/>
{note && <span>{note}</span>}
<ErrorMessage
name={name}
errors={errors}
render={() => {
return <p className="err-msg">pilih salah satu</p>;
}}
/>
</FormGroup>
);
}
Basically you need to handle the onChange of your react-select
const funcComponent = () => {
const [firstOptions, setFirstOptions] = useState({});
const [secondOptions, setSecondOptions] = useState({});
useEffect(() => {
//Here dispatch your defined actions to load first select options
setFirstOptions(response-data)
})
const handleFirstOptions = selectedVal => {
//Here dispatch your defined action to load second select options
setSecondOptions(response-data)
}
const handleSecondOptions = selectedVal => {
//Your action to perform
}
return (
<Label>First Option Field</Label>
<Select
options={firstOptions}
onChange={handleFirstOptions}
/>
Label>Second Option Field</Label>
<Select
options={secondOptions}
onChange={handleSecondOptions}
/>
)}

How to dispatch event in handleSubmit in withFormik

still new to formik and react hook.
Here is my code in react.
// react
import React, { useEffect } from 'react';
import { withFormik } from 'formik';
import { useDispatch } from 'redux-react-hook';
import { takeEvery, call, put } from 'redux-saga/effects';
// row, col, field, input, buttonGroup
import {
Row,
Col,
FieldWrapper,
Input,
ButtonGroup
} from 'some-tool';
const searchTypeOption = [
....
];
const SearchForm = (props: any) => {
const {
values,
touched,
errors,
handleChange,
handleSubmit,
} = props;
return (
<form onSubmit={handleSubmit}>
<Row>
<Col md="3">
<FieldWrapper required={true}>
<Select name="searchKey" onChange={handleChange} value={values.searchKey} options={searchTypeOption} />
</FieldWrapper>
{errors.searchKey && touched.searchKey && <div>{errors.searchKey}</div>}
</Col>
<Col md="5">
<FieldWrapper>
<Input
placeholder="Search"
type="text"
onChange={handleChange}
value={values.searchValue}
name="searchValue"
/>
</FieldWrapper>
{errors.searchValue && touched.searchValue && <div>{errors.searchValue}</div>}
</Col>
</Row>
<Row>
<ButtonGroup>
<Button>Clear</Button>
<Button type="submit">Search</Button>
</ButtonGroup>
</Row>
</form>
);
};
export const Search = withFormik({
mapPropsToValues: () => ({ searchKey: '', searchValue: '' }),
// Custom sync validation
validate: values => {
let errors = {};
//if (values.hasOwnProperty('searchKey') && !values.searchKey) {
// errors.searchKey = 'Required';
//}
return errors;
},
handleSubmit: (values, { props, setSubmitting }) => {
const payload = {
searchKey: values.searchKey,
searchValue: values.searchValue
};
// NOTE: obj.props is empty.....
console.log(obj);
// How to use dispatch here or some way to fire event
dispatch({ type: 'SEARCH_DOCS', payload: payload });
},
})(SearchForm);
in handleSubmit, how do I dispatch an event, so saga and redux are able to receive them?
In order to do that you must pass a connected component so you can have access to dispatch
wrap this with formik like you do
const SearchFormFormik = withFormik(SearchForm)
Then connect it to redux
const mapDispatchToProps = {
searchDocFun,
};
const ConnectedSearchForm = connect(
null,
mapDispatchToProps
)(SearchFormFormik);
Then you can use the searchDocFun on handle submit
handleSubmit: (values, { props, setSubmitting }) => {
props.searchDocFun(values)
}

Resources