Проблемы были:
- Компонент
LocalStorageFormControl
не обновил состояние, когда он получил начальное значение из localStorage.
input
не обновлял состояние onChange, поскольку у него не было обработчика onChange
.
- Компонент
CustomCheckboxGroup
не имеет опоры name
, которая используется как часть key
в localStorage
Решение следующее:
App.js
import React, { useEffect, useState } from "react";
// Bootstrap
import { Row, Col, Form } from "react-bootstrap";
import CustomCheckboxGroup from "./CustomCheckboxGroup";
// Function that calls all functions in order to allow the user to provide their own onChange, value etc
const callAll = (...fns) => (...args) => fns.forEach(fn => fn && fn(...args));
// Connect any <input /> to LocalStorage and let it manage value / onChange
function LocalStorageFormControl({
children,
formControl = React.Children.only(children),
lsKey = `lsfc:${formControl.props.name}`,
updateInitialState
}) {
const [hasChanged, setHasChanged] = useState(false);
const [value, setValue] = useState(() => {
return (
window.localStorage.getItem(lsKey) || formControl.props.defaultValue || ""
);
});
// Let the user control the value if needed
if (
formControl.props.value !== undefined &&
formControl.props.value !== value
) {
setValue(formControl.props.value);
}
useEffect(() => {
if (hasChanged) {
if (value) {
window.localStorage.setItem(lsKey, value);
} else {
window.localStorage.removeItem(lsKey);
}
} else {
if (value) {
// if hasChanged is false and there is value that means there was a value in localStorage
setHasChanged(true);
// update the state
updateInitialState(value);
}
}
}, [value, lsKey, hasChanged, updateInitialState]);
return React.cloneElement(React.Children.only(children), {
onChange: callAll(formControl.props.onChange, e => {
setHasChanged(true);
setValue(e.target.value);
}),
value,
defaultValue: undefined
});
}
const checkboxes = [
{
label: "Dhr",
name: "aanhef-dhr",
stateName: "salutation",
value: "De heer"
},
{
label: "Mevr",
name: "aanhef-mevr",
stateName: "salutation",
value: "Mevrouw"
}
];
export default function App() {
const [state, setState] = useState({});
function handleSubmit(e) {
e.preventDefault();
console.log("Handling submission of the form");
}
function onChange(e, stateName) {
e.persist();
setState(prevState => ({ ...prevState, [stateName]: e.target.value }));
}
// Log the state to the console
console.log(state);
return (
<Row>
<Col xs={12}>
<Form
id="appointment-form"
onSubmit={handleSubmit}
noValidate
style={{ marginBottom: 75 }}
>
<LocalStorageFormControl
updateInitialState={value => {
setState({ ...state, "test-textfield": value });
}}
>
{/* Add onChange handler to update the state with input value*/}
<input
type="text"
name="test-textfield"
onChange={e => {
setState({ ...state, "test-textfield": e.target.value });
}}
/>
</LocalStorageFormControl>
<LocalStorageFormControl
updateInitialState={value => {
setState({ ...state, salutation: value });
}}
>
<CustomCheckboxGroup
checkboxes={checkboxes}
key="salutation"
label="Salutation"
name="salutation"
onChange={(e, stateName) => onChange(e, stateName)}
required={true}
value={state.salutation}
/>
</LocalStorageFormControl>
</Form>
</Col>
</Row>
);
}
CustomCheckboxGroup.js
import React from "react";
// Bootstrap
import { Form, Row, Col } from "react-bootstrap";
export default ({ onChange, value, name, label, className, checkboxes }) => (
<Row>
<Col xs={12}>
<Form.Label>{label}</Form.Label>
</Col>
<Col>
<Form.Group className="d-flex flex-direction-column">
{checkboxes.map((checkbox, key) => {
return (
<div
key={key}
className={
checkbox.value === value
? "appointment_checkbox active mr-2 custom-control custom-checkbox"
: "appointment_checkbox mr-2 custom-control custom-checkbox"
}
>
<input
name={name}
type="checkbox"
value={checkbox.value}
onChange={e => onChange(e, checkbox.stateName)}
checked={value === checkbox.value}
id={"checkbox-" + checkbox.name}
className="custom-control-input"
/>
<label
className="custom-control-label"
htmlFor={"checkbox-" + checkbox.name}
>
{checkbox.label}
</label>
</div>
);
})}
</Form.Group>
</Col>
</Row>
);
У меня есть совет по поводу вашего кода:
- Используйте переключатели вместо флажков, если вы разрешаете пользователю выбирать только один вариант. https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/radio
- Вы можете сохранить весь объект состояния, если хотите, заменив это:
const [state, setState] = useState({});
на это: // Get the saved state in local storage if it exists or use an empty object
// You must use JSON.parse to convert the string back to a javascript object
const initialState = localStorage.getItem("form-state")
? JSON.parse(localStorage.getItem("form-state"))
: {};
// Initialize the state with initialState
const [state, setState] = useState(initialState);
// Whenever the state changes save it to local storage
// Notice that local storage accepts only strings so you have to use JSON.stringify
useEffect(() => {
localStorage.setItem("form-state", JSON.stringify(state));
}, [state]);
person
Ahmed Mokhtar
schedule
08.07.2020