I'm only now switching to styled component and could not find an answer for this fairly simple logic: if the validation for a field fails, it should have red borders.
With classic CSS classes:
const validateMail = (e) => {
let mail = e.target.value;
if (!mailRegex.test(mail)) {
e.target.classList.add("error");
return;
}
e.target.classList.remove("error");
}
How can I do something similar to e.target.classList.add("error")
? I assume I would have to, somehow, change this list to something that changes the style of the field to begin with. Here's the complete class of the input styled component that triggers this function:
const Field = styled.div`
display: flex;
flex-direction: column;
`;
const Label = styled.label`
font: ${fonts.body1};
font-size: .75rem;
`;
const Input = styled.input`
font: ${fonts.body1};
padding: 10px;
border-radius: 5px;
outline: none;
border: 1px solid rgba(0, 0, 0, 0.3);
width: 100%;
resize: none;
box-sizing: border-box;
&:focus {
border: 1px solid ${palette.primary_blue};
}
`;
function InputField(props) {
return (
<Field>
<Label htmlFor={"txt" + props.name}>{props.name + ":"}</Label>
<Input name={"txt" + props.name} onKeyUp={props.onKeyUp}></Input>
</Field>
);
}
export default InputField;
As you've identified, styled components don't follow the classic class list paradigm. There are several ways to achieve the result you're after. I've demonstrated 2.
You can pass a custom isValid
prop to the styled component like this:
<Input
isValid={isValid}
name={"txt" + props.name}
onKeyUp={props.onKeyUp}
/>
You can then add conditional rendering logic inside the styled component based on how the prop is evaluated.
Note that the following arrow function in the styled component definition (in place of styled.input
) ensures that the prop is treated as a component prop and not as an actual DOM element attribute (which would not be valid markup and could cause errors):
const Input = styled(({
isValid,
...props
}) => <input {...props} />)`
border: 1px solid ${({ isValid }) =>
isValid ? `rgba(0, 0, 0, 0.3)` : `red`)};
/* ... the rest of your styles... */
`;
For a live example of the above, see this Code Sandbox. Note line 41 where isValid
is being set to false
as a stand-in for actual validation logic. Try changing it to true
and you'll see that the border color changes.
const Input = styled.input`
border: 1px solid rgba(0, 0, 0, 0.3);
/* ... the rest of your styles... */
`;
const ErrorInput = styled(Input)`
border-color: red;
`;
const StyledInput = isValid ? Input : ErrorInput;
return (
<Field>
<Label htmlFor={"txt" + props.name}>{props.name + ":"}</Label>
<StyledInput
name={"txt" + props.name}
onKeyUp={props.onKeyUp}
/>
</Field>
);
Here's a Code Sandbox of this approach. Same as with the first approach, see line 45 for hard-coded isValid
value.
I prefer the first solution because it allows for smooth transitions between changing values (like with classes). The second approach works fine for static rendered elements, but it will basically destroy/create a new element on the fly when the component reference changes from Input
to ErrorInput
. If you want smooth transitions then you should use the first approach.