Несколько вещей:
- нет необходимости хранить
matching
в состоянии, его можно получить из значения текстового поля уполномоченного и списка пользователей
- текстовое поле правопреемника (я называю его
searchAssignee
) и список выбранных правопреемников (assignees
) по существу являются двумя разными входными данными, поэтому они должны быть отдельными входными данными Formik
- разделение двух указанных выше входных данных устраняет проблему с фокусом
Вся идея formik заключается в том, что он устраняет необходимость хранить входные значения в состоянии. Я удалил ненужное состояние и разделил входные данные выше.
Рабочий пример
const getMatching = (users, keyword) => {
if (!keyword) return null;
const match = users.find(({ name }) => name.startsWith(keyword));
if (!match) return null;
return match.name;
};
const FormContainer = ({ users }) => {
return (
<Formik
initialValues={{
summary: "",
description: "",
searchAssignee: "",
assignees: []
}}
onSubmit={async (values) => {
console.log(values);
}}
>
<Form>
<Row>
<Col>
<div className="textcolor">Summary</div>
<Field
id="summary"
name="summary"
placeholder="Add a short summary for your task"
component={({
field, // { name, value, onChange, onBlur }
form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
...props
}) => {
return (
<input
className="input-fields"
{...props}
// {...form}
{...field}
/>
);
}}
/>
</Col>
</Row>
<Row>
<Col>
<div className="textcolor">Description</div>
<Field
id="description"
name="description"
placeholder="Description"
component={({
field, // { name, value, onChange, onBlur }
form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
...props
}) => {
return (
<ReactQuill
theme="snow"
value={field.value}
onChange={field.onChange(field.name)}
style={{ minHeight: "5rem" }}
{...field}
{...props}
/>
);
}}
/>
</Col>
</Row>
<Row>
<Col>
<div className="textcolor">Assignees</div>
<Col className="assignee-wrapper d-flex">
<div className="d-flex">
<Field
id="assignees"
name="assignees"
component={({
field, // { name, value, onChange, onBlur }
form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
...props
}) => (
<>
{field.value.map((item) => (
<div
className="assignee-tag d-flex"
onClick={() => {
const newAssignees = field.value.filter(
(val) => val !== item
);
form.setFieldValue(field.name, newAssignees);
}}
>
<div>{item}</div>
<GrFormClose />
</div>
))}
</>
)}
/>
</div>
<div className="d-flex col">
<Field
id="searchAssignee"
name="searchAssignee"
placeholder="Type names of assignees to start getting suggestions"
component={({
field, // { name, value, onChange, onBlur }
form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
...props
}) => {
const suggestion = getMatching(users, field.value);
return (
<>
<input
className="input-fields"
{...props}
{...form}
{...field}
/>
{suggestion && (
<span
className="text-nowrap"
onClick={() => {
if (!form.values.assignees.includes(suggestion)) {
form.setFieldValue("assignees", [
...form.values.assignees,
suggestion
]);
}
}}
>
{suggestion}
</span>
)}
</>
);
}}
/>
</div>
</Col>
</Col>
</Row>
<div className="mt-2 float-end">
<button className="btn btn-dark btn-sm" type="submit">
Add
</button>
<button className="btn btn-light btn-sm ms-2" type="reset">
Cancel
</button>
</div>
</Form>
</Formik>
);
};
const App = ({ isModalVisible, setModalVisible }) => {
const [users, setUsers] = useState([
{ name: "Hello World" },
{ name: "Foo Bar" }
]);
return (
<Modal show={true}>
<Modal.Body>
<Row>
<div>
<GrFormClose
className="float-end"
onClick={() => setModalVisible(false)}
style={{ cursor: "pointer" }}
/>
</div>
<div>
<FormContainer
users={users}
/>
</div>
</Row>
</Modal.Body>
</Modal>
);
};
Вы можете добавить свой полный код в песочницу?
конечно позвольте мне добавить
использовать рендеринг вместо компонента в элементе
Field