The code below is using antd v3,for the antd v4 the form.create() is already not available, I read the documentation,it shows to replace the form.create() by useform.I tried to put it in the code(the second code) but still not working. Hhow can I use useForm in the code?
import React from 'react'
import {Form, Input} from 'antd'
const EditableContext = React.createContext();
const EditableRow = ({ form, index, ...props }) => (
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
);
export const EditableFormRow = Form.create()(EditableRow);
export class EditableCell extends React.Component {
state = {
editing: false,
};
toggleEdit = () => {
const editing = !this.state.editing;
this.setState({ editing }, () => {
if (editing) {
this.input.focus();
}
});
};
save = e => {
const { record, handleSave } = this.props;
this.form.validateFields((error, values) => {
if (error && error[e.currentTarget.id]) {
return;
}
this.toggleEdit();
handleSave({ ...record, ...values });
});
};
}
The useForm code
export const EditableFormRow = () => {
const { form, index, ...props } = useForm();
return (
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
)
};
useForm() return array, you can get form like this:
import React from "react";
import ReactDOM from "react-dom";
import { Form, Input, Button } from "antd";
const App = () => {
return <>{["a#x.com", "b#x.com"].map((email) => EditRow({ email }))}</>;
};
const layout = {
labelCol: { span: 8 },
wrapperCol: { span: 16 }
};
const tailLayout = {
wrapperCol: { offset: 8, span: 16 }
};
const EditRow = ({ email }) => {
const [form] = Form.useForm();
const onFinish = (values) => {
console.log(values);
};
const onClick = () => {
console.log(form.getFieldValue("email"));
};
return (
<Form form={form} layout={layout} onFinish={onFinish}>
<Form.Item
name="email"
label="email"
rules={[{ required: true }]}
initialValue={email}
>
<Input />
</Form.Item>
<Form.Item {...tailLayout}>
<Button type="primary" htmlType="submit">
Submit
</Button>
<Button onClick={onClick}>getFieldValue</Button>
</Form.Item>
</Form>
);
};
Related
I have a container component that works with Redux, in it I determined the state of the text field and throw it into the form component that works with Formik
But I have such a problem that after clicking the button, the text field is not cleared
How do I fix this ? I've seen a lot about resetForm() from Formik, but I can't work with it
TaskInput.jsx
export const TaskInput = ({ value, onChange, onAdd }) => {
const formik = useFormik({
initialValues: {
text: value,
},
})
return (
<Container>
<Formik>
<FormWrapper onSubmit={onAdd}>
<Input
id="task"
type="text"
placeholder="Add a task"
value={formik.text}
onChange={onChange}
/>
<AddButton type="submit">Add</AddButton>
</FormWrapper>
</Formik>
</Container>
)
}
Task.jsx(component container)
class Task extends PureComponent {
constructor(props) {
super(props)
this.state = {
taskText: '',
}
}
handleInputChange = e => {
this.setState({
taskText: e.target.value,
})
}
handleAddTask = () => {
const { taskText } = this.state
const { addTask } = this.props
addTask(uuidv4(), taskText, false)
this.setState({
taskText: '',
})
}
render() {
const { taskText } = this.state
return (
<>
<TaskInput
onAdd={this.handleAddTask}
onChange={this.handleInputChange}
value={taskText}
/>
...
</>
)
}
}
const mapStateToProps = ({ tasks }) => {
return {
tasks,
}
}
export default connect(mapStateToProps, {
addTask,
removeTask,
completeTask,
editTask,
})(Task)
Solution
export const TaskInput = ({ value, onChange, onAdd }) => {
const inputRef = useRef(null)
const formik = useFormik({
initialValues: {
text: value,
},
})
const onSubmit = () => {
onAdd(),
inputRef.current.value = ''
}
return (
<Container>
<Formik>
<FormWrapper onSubmit={onSubmit}>
<Input
ref={inputRef}
id="task"
type="text"
placeholder="Add a task"
value={formik.text}
onChange={onChange}
/>
<AddButton type="submit">Add</AddButton>
</FormWrapper>
</Formik>
</Container>
)
}
my application consists of a filter select by provinces, the filter is a modal that shows me the result in the filter component (child), I would like to show the result in the parent not in the filter modal. I do not know what to pass to the parent to show the result or how to show it on the screen.
///parent component
import React, { useState, useEffect } from 'react'
import { useDispatch } from "react-redux";
import { getClinic } from '../../api/drupalAPI'
import {Clinic} from '#icofcv/common';
import { selectClinics } from '../../actions/detailClinics'
import { useNavigate } from "react-router-dom";
import contentUtils from '../../lib/contentUtils'
import { SearchFilterClinics } from './SearchFilterClinics'
import Button from 'react-bootstrap/Button';
import Form from 'react-bootstrap/Form';
const ClinicList = () => {
const [clinicList, setClinicList] = useState<Clinic[]>([]);
const [clinicListFiltered, setClinicListFiltered] = useState<Clinic[]>([]);
const [searchClinic, setSearchClinic] = useState("");
const dispatch = useDispatch();
const navigate = useNavigate();
///modal control
const [isOpen, setIsOpen] = useState(false);
const openModal = () => setIsOpen(true);
const closeModal = () => setIsOpen(false);
console.log(searchClinic);
const fetchClinicList = async () => {
getClinic().then((response)=>{
console.log(response)
setClinicList(response);
setClinicListFiltered(response)
}).catch ( (error) => {
console.error(error);
throw error;
});
}
const handleChange=e=>{
setSearchClinic(e.target.value);
filter(e.target.value);
}
const filter=(termSearch)=>{
const resultSearch= clinicList.filter((element)=>{
if(element.title.toString().toLowerCase().includes(termSearch.toLowerCase())
){
return element;
}
});
setClinicListFiltered(resultSearch);
}
function handleAddToDetail(clinic) {
dispatch(selectClinics(clinic));
navigate('clinicdetail');
}
function goToPageSearchFilterClinics() {
navigate('filterclinics');
}
useEffect (() => {
fetchClinicList();
}, []);
return(
<>
<div style={{display: 'flex'}}>
<div style={{width:'5rem'}}>
{/* <button onClick={() => goToPageSearchFilterClinics()}>filtro</button> */}
<button onClick={openModal}>filtro</button>
</div>
< SearchFilterClinics isOpen={isOpen} closeModal={closeModal} clinicFilter={clinicFilter}></SearchFilterClinics>
<Form className="d-flex">
<Form.Control
type="search"
value={searchClinic}
placeholder="Search"
className="me-2"
aria-label="Search"
onChange={handleChange}
/>
</Form>
</div>
<div className="content-cliniclist">
{
clinicListFiltered.map((clinic) => (
<div style={{marginBottom: '3rem'}}>
<button
type="button"
onClick={() => handleAddToDetail(clinic)}
style={{all: 'unset'}}
>
<div>
{/* <img src={ contentUtils.getLargeImageUrl(clinic.logoWidth )} alt="#"></img> */}
<div>{clinic.title}</div>
<div>{clinic.propsPhone}</div>
<div>{clinic.mobile}</div>
<div>{clinic.email}</div>
<div>{clinic.registry}</div>
</div>
</button>
</div>
))
}
</div>
</>
)
}
export default ClinicList;
////child component
import React, { useState, useEffect } from 'react'
import Select, { SingleValue } from 'react-select'
import { getClinic } from '../../api/drupalAPI'
import {Clinic} from '#icofcv/common';
import "./Modal.css";
interface Props {
isOpen: boolean,
clinicFilter: String,
closeModal: () => void
}
export const SearchFilterClinics : React.FC<Props> = ({ children, isOpen, closeModal, clinicFilter }) => {
////filter
type OptionType = {
value: string;
label: string;
};
const provincesList: OptionType[] = [
{ value: 'Todos', label: 'Todos' },
{ value: 'Valencia', label: 'Valencia' },
{ value: 'Castellon', label: 'Castellon' },
{ value: 'Alicante', label: 'Alicante' },
]
const [clinicList, setClinicList] = useState<Clinic[]>([]);
const [clinicListFilteredSelect, setClinicListFilteredSelect] = useState<Clinic[]>([]);
const [filterSelectClinic, setFilterSelectClinic] = useState<SingleValue<OptionType>>(provincesList[0]);
const handleChangeSelect = async (provinceList: SingleValue<OptionType>) => {
getClinic().then((response) => {
setClinicList(response);
setClinicListFilteredSelect(response)
setFilterSelectClinic(provinceList);
filterSelect(provinceList );
}).catch ((error) => {
console.error(error);
throw error;
});
}
const filterSelect=(termSearch)=>{
const resultFilterSelect = clinicList.filter((element) => {
if(element.province?.toString().toLowerCase().includes(termSearch.value.toLowerCase() )
){
return element;
}
});
setClinicListFilteredSelect(resultFilterSelect);
}
const handleModalContainerClick = (e) => e.stopPropagation();
return (
<>
<div className={`modal ${isOpen && "is-open"}`} onClick={closeModal}>
<div className="modal-container" onClick={handleModalContainerClick}>
<button className="modal-close" onClick={closeModal}>x</button>
{children}
<div>
<h1>Encuentra tu clΓnica</h1>
</div>
<div>
<form>
<label>Provincia</label>
<Select
defaultValue={filterSelectClinic}
options={provincesList}
onChange={handleChangeSelect}
/>
</form>
{
clinicListFilteredSelect.map((clinicFilter) => (
<div>
<div>{clinicFilter.title}</div>
<div>{clinicFilter.propsPhone}</div>
<div>{clinicFilter.mobile}</div>
<div>{clinicFilter.email}</div>
<div>{clinicFilter.province} </div>
<div>{clinicFilter.registry}</div>
</div>
))
}
</div>
</div>
</div>
</>
)
}
You need to pass a callback function to the parent and set the state value in the parent, not in the child. (This is known as lifting state up.)
In your case this would involve moving
const [filterSelectClinic, setFilterSelectClinic] = useState<SingleValue<OptionType>>(provincesList[0]);
Into the parent component. Then passing the setFilterSelectClinic function into the child component.
<SearchFilterClinics setFilterSelectClinic={(value) => setFilterSelectClinic(value)} isOpen={isOpen} closeModal={closeModal} clinicFilter={clinicFilter}/>
The value (within the parenthesis) is being passed up by the child component. This is the value you set here:
getClinic().then((response) => {
...
// this is the function we pass in from the parent. It set's the value
// of the callback function to provinceList
setFilterSelectClinic(provinceList);
...
We then setFilterSelectClinic to that value. Meaning filterSelectClinic now has the value passed up in the callback.
I need to fix a memory leak in my app but Im not sure how to. I have a component that uses a modal and I get the error when I am adding an item. The modal is reusable and I use it in other components as well. This is the main component:
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { Card, Select, Form, Button } from 'antd';
import Table from 'components/Table';
import Modal from '../Modal';
import styles from '../index.module.scss';
const { Item } = Form;
const { Option } = Select;
const PersonForm = ({ details, form }) => {
const [modalVisible, setModalVisible] = useState(false);
const [name, setName] = useState(
details?.name ? [...details?.name] : []
);
useEffect(() => {
form.setFieldsValue({
name: name || [],
});
}, [form, details, name]);
const addName = values => {
setName([...name, values]);
setModalVisible(false);
};
const removeName = obj => {
setName([...name.filter(i => i !== obj)]);
};
const cancelModal = () => {
setModalVisible(false);
};
return (
<div>
<Card
title="Names
extra={
<Button type="solid" onClick={() => setModalVisible(true)}>
Add Name
</Button>
}
>
<Table
tableData={name}
dataIndex="name"
removeName={removeName}
/>
</Card>
<Item name="name">
<Modal
title="Add Name"
fieldName="name"
onSubmit={addName}
visible={modalVisible}
closeModal={cancelModal}
/>
</Item>
</div>
);
};
PersonForm.propTypes = {
details: PropTypes.instanceOf(Object),
form: PropTypes.instanceOf(Object),
};
PersonForm.defaultProps = {
form: null,
details: {},
};
export default PersonForm;
And this is the modal component:
import React from 'react';
import PropTypes from 'prop-types';
import { Input, Form } from 'antd';
import Modal from 'components/Modal';
import LocaleItem from 'components/LocaleItem';
const { Item } = Form;
const FormModal = ({ visible, closeModal, onSubmit, fieldName, title }) => {
const [form] = Form.useForm();
const layout = {
labelCol: { span: 8 },
wrapperCol: { span: 15 },
};
const addItem = () => {
form
.validateFields()
.then(values => {
onSubmit(values, fieldName);
form.resetFields();
closeModal(fieldName);
})
.catch(() => {});
};
const canceledModal = () => {
form.resetFields();
closeModal(fieldName);
};
return (
<Modal
onSuccess={addItem}
onCancel={canceledModal}
visible={visible}
title={title}
content={
<Form {...layout} form={form}>
<Item
name="dupleName"
label="Name:"
rules={[
{
required: true,
message: 'Name field cannot be empty',
},
]}
>
<Input placeholder="Enter a name" />
</Item>
</Form>
}
/>
);
};
FormModal.propTypes = {
visible: PropTypes.bool.isRequired,
closeModal: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
fieldName: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
};
FormModal.defaultProps = {};
export default FormModal;
I get a memory leak when I am in the test file when adding items in the modal. Can someone point out why this is happening and how to fix this? Thanks
Remove closeModal and form.resetFields from addItem function.
const addItem = () => {
form
.validateFields()
.then(values => {
onSubmit(values, fieldName); // when this onSubmit resolves it closes the modal, therefor these two lines below will be executed when component is unmounted, causing the memory leak warning
form.resetFields();
closeModal(fieldName);
})
.catch(() => {});
};
// instead maybe just:
const [form] = Form.useForm();
<Modal onOk={form.submit}>
<Form form={form}>
<Form.Item name="foo" rules={[{ required: true }]}>
<Input />
</Form.Item>
</Form>
</Modal>
Also, as far as I know you don't need to call form.validateFields as Ant Design's Form would do that automatically if rules are set in the Form.Item's.
I'm having issues integrating Draft.js with Material UI's TextField component (or Input). I followed the documentation (https://material-ui.com/components/text-fields/#integration-with-3rd-party-input-libraries) but it still fails. I've integrated another package using the same method before which worked but there are issues with integrating Draft.js.
Code below:
import React, { useState, useRef, forwardRef } from "react";
import Editor, { createEditorStateWithText } from "draft-js-plugins-editor";
import createEmojiPlugin from "draft-js-emoji-plugin";
import { TextField, makeStyles } from "#material-ui/core";
import { isObject } from "lodash";
import { findDOMNode } from "react-dom";
import classNames from 'classnames';
function InputWrapper(props) {
const { component: Component, inputRef, ...other } = props;
const ref = useRef();
React.useImperativeHandle(inputRef, () => ({
focus: () => {
findDOMNode(inputRef.current).focus();
},
}));
return <Component ref={ref} {...other} />;
}
const emojiPlugin = createEmojiPlugin();
const { EmojiSuggestions, EmojiSelect } = emojiPlugin;
const plugins = [emojiPlugin];
const text = `Cool, we can have all sorts of Emojis here. π
πΏβοΈππ aaaand maybe a few more here π²βοΈπ» Quite fun!`;
const useStyles = makeStyles((theme) => ({
textField: {
width: "100%",
minHeight: 31,
tabSize: 8,
fontVariantLigatures: "none",
boxSizing: "content-box",
userSelect: "text",
whiteSpace: "pre-wrap",
},
}));
let DraftInput = (props, ref) => {
const {
InputProps,
InputLabelProps,
value,
handleEnterKeyPress,
onChange,
id,
...other
} = props;
const [editorState, setEditorState] = useState(
createEditorStateWithText(text)
);
const baseClasses = useStyles();
const handleChange = (state) => {
setEditorState(state);
// onChange(state);
};
const propClass =
isObject(InputProps) && InputProps.hasOwnProperty("className")
? InputProps.className
: false;
const classes = classNames([baseClasses, propClass, "emoji-input"]);
return (
<div>
<TextField
inputRef={ref}
autoFocus={true}
value={value}
InputProps={{
inputComponent: InputWrapper,
inputProps: {
component: Editor,
onChange: handleChange,
editorState,
},
...InputProps,
}}
InputLabelProps={{
...InputLabelProps,
}}
{...InputProps}
{...other}
/>
</div>
);
};
DraftInput = forwardRef(DraftInput);
export default DraftInput;
Note: there's still test data being used.
This is how I got it to work.
import Editor from "#draft-js-plugins/editor";
const DraftField = React.forwardRef(function DraftField(props, ref) {
const { component: Component, editorRef, handleOnChange, ...rest } = props;
React.useImperativeHandle(ref, () => ({
focus: () => {
editorRef?.current?.focus();
},
}));
return <Component {...rest} ref={editorRef} onChange={handleOnChange} />;
});
const DraftEditor = () => {
const [editorState, setEditorState] = React.useState();
const editorRef = React.useRef(null);
const handleOnChange = (newEditorState) => {
setEditorState(newEditorState);
};
return (
<TextField
fullWidth
label="Content"
InputProps={{
inputProps: {
component: Editor,
editorRef,
editorState,
handleOnChange
},
inputComponent: DraftField,
}}
/>
);
};
I want to add validation in my react form I am using SimpleReactValidator library for validation, but once I setup the code, the error is not displaying. But when i add
{validator.showMessages('fullName', fullName, 'required|alpha')}
before the return statement it's showing me without click on submit button.
Here's my code
import React, { useState } from 'react';
import SimpleReactValidator from 'simple-react-validator';
const UserDetails = ({ setForm, formData, navigation }) => {
const {
fullName
}= formData;
const useForceUpdate = () => useState()[1];
const validator = new SimpleReactValidator();
const forceUpdate = useForceUpdate();
const submitForm = (e) =>{
e.preventDefault()
if (validator.allValid()) {
alert('You submitted the form and stuff!');
} else {
validator.showMessages();
forceUpdate();
}
}
return(
<>
<input
type="text"
name="fullName"
placeholder='Name'
onChange={setForm}
defaultValue={fullName}
/>
{validator.message('fullName', fullName, 'required|alpha')}
</>
);
}
export default UserDetails;
Hope this will help you
import React, { useState } from 'react';
import SimpleReactValidator from 'simple-react-validator';
const UserDetails = () => {
const validator = new SimpleReactValidator();
const [state, setState] = useState({
fullName:""
})
const handleChnage = (e) => {
setState({
fullName:e.target.value
})
}
const submitForm = () => {
if (validator.allValid()) {
alert('You submitted the form and stuff!');
} else {
validator.showMessages();
}
}
return (
<>
<input
type="text"
name="fullName"
placeholder='Name'
onChange={(e) => handleChnage(e)}
defaultValue={state.fullName}
/>
{validator.message('fullName', state.fullName, 'required|alpha')}
<button onClick={() => submitForm()}>submit</button>
</>
);
}
export default UserDetails;
This code example would solve your problem.
class App extends React.Component {
constructor(props){
super(props)
this.validator = new SimpleReactValidator({autoForceUpdate: this});
this.state = {
fullName: ''
};
}
handleFullNameChange(e) {
this.setState({fullName: e.target.value});
}
handleFullNameBlur() {
if(this.validator.allValid()) {
this.validator.hideMessages();
} else {
this.validator.showMessages();
}
}
render(){
return (
<div>
<input type="text" name="fullName" placeholder='Name' onChange={this.handleFullNameChange.bind(this)} onBlur={this.handleFullNameBlur.bind(this)} value={this.state.fullName} />
{this.validator.message('fullName', this.state.fullName, 'required|alpha')}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'))
You can also take a look on Codepen here: https://codepen.io/aptarmy/pen/oNXRezg