DEV Community

Cover image for Introduction to Jotai. Derived atoms. Controlling UI
Andrzej Fricze
Andrzej Fricze

Posted on

Introduction to Jotai. Derived atoms. Controlling UI

Intro to Jotai. Part 1
Intro to Jotai. Part 2

Creating derived atoms is easy and useful. From one data source you can create many atoms. They allow to transform source data, or create new information based on it. Derived atoms are your first tool to structure your data models, and to control your UI state.

You already created simple form with one input and validation. Now let’s add another input with validation and submit button. Later, we can use validation atoms to decide whether user can submit the form. In this part, you’ll also switch from bare HTML elements to Material UI.

import TextField from "@mui/material/TextField";

const Input = ({ type = "text", label, sourceAtom }: InputProps) => {
  const [text, setText] = useAtom(sourceAtom);
  return (
    <TextField
      type={type}
      label={label}
      variant="filled"
      value={text}
      onChange={(e) => setText(e.target.value)}
    />
  );
};
Enter fullscreen mode Exit fullscreen mode

Let’s use new version of Input component and create inputs for name and password.

const nameAtom = atom("John");
const passwordAtom = atom("");

const Form = () => {
  return (
    <Box
      component="form"
      sx={{
        "& > :not(style)": { m: 2, width: "25ch" }
      }}
    >
      <Typography variant="h3" gutterBottom>
        Registration form
      </Typography>

      <Input label="Name" sourceAtom={nameAtom} />
      <Input type="password" label="Password" sourceAtom={passwordAtom} />
    </Box>
  );
};
Enter fullscreen mode Exit fullscreen mode

Source atoms are ready. Next step is creating derived validation atoms.

const nameLengthAtom = atom((get) => get(nameAtom).length);
const passwordLengthAtom = atom((get) => get(passwordAtom).length);

const isNameValid = atom((get) => get(nameLengthAtom) >= 3);
const isPasswordValid = atom((get) => get(passwordLengthAtom) >= 3);
Enter fullscreen mode Exit fullscreen mode

Because you want to control whether user can submit a form we need one more atom. It’s a general atom describing whether whole form is in valid state.

const isFormValid = atom((get) => get(isNameValid) && get(isPasswordValid));
Enter fullscreen mode Exit fullscreen mode

This atom derives values from name and password validation atoms and joins those values together. Using this new atom you’re ready to create submit button.

const SubmitButton = () => {
  const isValid = useAtomValue(isFormValid);

  return (
    <Box>
      <Button disabled={!isValid} variant="outlined">
        Register
      </Button>
    </Box>
  );
};
Enter fullscreen mode Exit fullscreen mode

If form is valid submit button is ready to click. If form is invalid button is disabled. Simple.

Let’s see how full form looks now.

const Form = () => (
  <Box
    component="form"
    sx={{
      "& > :not(style)": { m: 2, width: "25ch" }
    }}
  >
    <Typography variant="h3" gutterBottom>
      Registration form
    </Typography>

    <Input label="Name" sourceAtom={nameAtom} />
    <Input type="password" label="Password" sourceAtom={passwordAtom} />

    <Error sourceAtom={isNameValid} errorInfo="Name is too short" />
    <Error sourceAtom={isPasswordValid} errorInfo="Password is too short" />

    <SubmitButton />
  </Box>
);
Enter fullscreen mode Exit fullscreen mode

This form also uses new, general Error component to display particular errors. Check out full code to see source of this component.

As always check out Jotai website for docs and more examples of Jotai code.

Top comments (0)