State Management
NRP repositories use multiple state management approaches depending on the complexity and scope of your component. Understanding when to use each approach is key to building maintainable React components.
Choosing the Right Approach
| Approach | Use Case | Complexity |
|---|---|---|
| React Built-in State | Simple component state (toggles, form inputs) | Low |
| Formik | Form handling with validation | Medium |
| React Context API | Cross-component data sharing | Medium |
| Redux | Complex app state managed across many components | High |
React Built-in State
For simple local state within a component, use the useState hook:
import { useState } from "react";
export const MyComponent = () => {
const [isOpen, setIsOpen] = useState(false);
const [count, setCount] = useState(0);
return (
<button onClick={() => setIsOpen(!isOpen)}>
{isOpen ? "Close" : "Open"}
</button>
);
};Use for:
- Simple toggles and switches
- Form input values
- Local component state that doesn’t need to be shared
Formik
Formik handles complex form state, validation, and submission logic. It’s used with react-invenio-forms for deposit forms in NRP repositories.
Use for:
- Deposit forms
- Multi-step workflows
- Forms with complex validation rules
- Any form with multiple fields
Validation with Yup
Yup is a JavaScript schema builder for value parsing and validation. It’s commonly used with Formik to define validation schemas for deposit forms.
import { Formik, Form, Field } from "formik";
import { TextField } from "@js/oarepo_ui/forms";
import * as yup from "yup";
const validationSchema = yup.object().shape({
title: yup.string().required("Title is required").min(3, "Title must be at least 3 characters"),
description: yup.string().required("Description is required"),
year: yup.number().required().min(1900).max(new Date().getFullYear()),
});
export const MyForm = () => {
return (
<Formik
initialValues={{ title: "", description: "", year: "" }}
validationSchema={validationSchema}
onSubmit={(values) => console.log(values)}
>
<Form>
<TextField fieldPath="title" />
<TextField fieldPath="description" required />
<TextField fieldPath="year" dataType="integer" />
<button type="submit">Submit</button>
</Form>
</Formik>
);
};Formik Hooks
Accessing Form Context
Use useFormikContext() to access the current form’s state and helpers:
import { useFormikContext, getIn } from "formik";
const MyComponent = () => {
const { values, errors, touched, isValid, isSubmitting } = useFormikContext();
// Get a field value
const titleValue = values.title;
// Check if there's an error on a field
const hasTitleError = errors.title && touched.title;
// Set a field value programmatically
const handleChange = () => {
// You'd typically call this in response to some event
// setFieldValue('fieldName', newValue)
};
return <div>{/* ... */}</div>;
};Available properties:
| Property | Description |
|---|---|
values | Object containing all field values |
errors | Object containing validation errors |
touched | Object tracking which fields have been touched |
isValid | Boolean indicating if the form is valid |
isSubmitting | Boolean indicating if form is currently submitting |
resetForm() | Function to reset the form to initial values |
setFieldValue(name, value) | Function to update a specific field value |
setFieldTouched(name, isTouched) | Function to mark a field as touched |
setErrors(errors) | Function to set validation errors |
Common Patterns
Get and set form field value:
const MyComponent = () => {
const { values, setFieldValue } = useFormikContext();
const currentValue = values.myField;
const updateValue = () => setFieldValue("myField", "new value");
};Check for errors and display:
const { errors, touched } = useFormikContext();
const myError = touched.myField ? errors.myField : undefined;Reset form to initial values:
const { resetForm } = useFormikContext();
const handleReset = () => resetForm();Official Formik documentation and guides.
Formik DocumentationAvailable form components in NRP repositories.
Form ComponentsReact Context API
Context API shares state across component trees without prop drilling. Use it for moderately complex state that needs to be accessed by multiple components.
import { createContext, useContext, useState } from "react";
const MyContext = createContext();
export const MyProvider = ({ children }) => {
const [sharedState, setSharedState] = useState(null);
return (
<MyContext.Provider value={{ sharedState, setSharedState }}>
{children}
</MyContext.Provider>
);
};
export const useMyContext = () => useContext(MyContext);Use for:
- User preferences (theme, language)
- Authentication state
- Data shared between sibling components
- State that doesn’t warrant Redux
Redux
Redux provides a predictable state container for complex application state. It’s used extensively in NRP repositories for:
- Search applications - Managing search state, filters, and results
- Deposit forms - Handling form submission and validation flows
Basic Redux Setup
import { configureStore, createSlice } from "@reduxjs/toolkit";
import { Provider, useSelector, useDispatch } from "react-redux";
// Create a slice
const searchSlice = createSlice({
name: "search",
initialState: { query: "", results: [] },
reducers: {
setQuery: (state, action) => {
state.query = action.payload;
},
},
});
// Configure store
const store = configureStore({
reducer: {
search: searchSlice.reducer,
},
});
// Use in components
export const SearchComponent = () => {
const query = useSelector((state) => state.search.query);
const dispatch = useDispatch();
return (
<input
value={query}
onChange={(e) => dispatch(searchSlice.actions.setQuery(e.target.value))}
/>
);
};When to Use Redux
- State is used by many components across the application
- State needs to be updated from many different places
- App state is complex and has nested or related data
- You need to track state changes over time (time-travel debugging)
Start with simpler approaches. Only introduce Redux when you’ve identified that your state management needs exceed what hooks or Context API can handle reasonably.
The recommended way to write Redux logic.
Redux Toolkit DocumentationOfficial React bindings for Redux.
React Redux Documentation