Extend functionality - Detailed
It's not as complicated as it first seems
Extending the functionality in redux is the hardest part of this process, at the end there is an example of how to use Redux in your component, that part is fairly easy.
Step 1: Create a New Slice File
Create a new file for your slice, e.g., newFeatureSlice.ts
, inside the src/client/src/store/
directory.
Step 2: Import Dependencies
Import the necessary modules from Redux Toolkit and any other dependencies you may need.
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
Step 3: Define Initial State
Define the initial state for your slice. This can be an object, array, or any other data type.
const initialState = {
data: null,
loading: false,
error: null,
};
Step 4: Create the Slice
Use the createSlice
function to create a slice. Define your reducers within the reducers
object.
const newFeatureSlice = createSlice({
name: "newFeature",
initialState,
reducers: {
fetchDataStart: (state) => {
state.loading = true;
},
fetchDataSuccess: (state, action: PayloadAction<any>) => {
state.data = action.payload;
state.loading = false;
},
fetchDataFailure: (state, action: PayloadAction<string>) => {
state.error = action.payload;
state.loading = false;
},
},
});
Step 5: Export Actions
Export the actions generated by createSlice
.
export const { fetchDataStart, fetchDataSuccess, fetchDataFailure } =
newFeatureSlice.actions;
Step 6: Export Reducer
Export the reducer so it can be combined with other reducers in the store.
export default newFeatureSlice.reducer;
Step 7: Update Root Reducer
In your commonReducer.ts
or equivalent, import the new reducer and add it to the root reducer.
import newFeatureReducer from "./newFeatureSlice";
const rootReducer = combineReducers({
// ...other reducers,
newFeature: newFeatureReducer,
});
Step 8: Create and Export Selectors
Create selectors to read specific pieces of state. You can place these in a selectors.ts
file or within your slice file.
export const selectNewFeatureData = (state: RootState) => state.newFeature.data;
Step 9: Use Actions in Components
Now you can use the actions and selectors in your React components using useDispatch
and useSelector
.
const dispatch = useDispatch();
const data = useSelector(selectNewFeatureData);
dispatch(fetchDataStart());
New Redux component
Below is a demo component that can serve as a new tab in the InspectorModule
. This component, named DemoTab
, demonstrates how to properly handle state changes in Redux without direct state mutations.
DemoTab.tsx
import React, { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { updateProject } from "../../../store/reducers/projectReducer"; // Import your projectReducer
import { RootState } from "../../../store"; // Import your RootState
const DemoTab: React.FC = () => {
const dispatch = useDispatch();
const projectState = useSelector((state: RootState) => state.project); // Replace with your project state selector
const [newLabel, setNewLabel] = useState("");
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setNewLabel(e.target.value);
};
const updateLabel = () => {
// Create a copy of the projectState and update the label
const updatedProjectState = {
...projectState,
label: newLabel,
};
// Dispatch the updated project state
dispatch(updateProject(updatedProjectState));
};
return (
<div>
<h2>Demo Tab</h2>
<input
type="text"
placeholder="New Label"
value={newLabel}
onChange={handleInputChange}
/>
<button onClick={updateLabel}>Update Label</button>
</div>
);
};
export default DemoTab;
Adding DemoTab to InspectorModule.tsx
To add this new DemoTab
to the InspectorModule
, you can update the InspectorModule.tsx
file as follows:
- Import
DemoTab
at the top of yourInspectorModule.tsx
:
import DemoTab from "./tabs/DemoTab";
- Add a new case for
DemoTab
in theswitch
statement that renders the tabs:
switch (selectedTab) {
case "admin":
return <AdminTab />;
case "attribute":
return <AttributeTab />;
case "terminal":
return <TerminalTab />;
case "demo": // New case
return <DemoTab />;
default:
return null;
}
- Update the
useState
andonTabChange
to include the new 'demo' tab:
const [selectedTab, setSelectedTab] = useState<
"admin" | "attribute" | "terminal" | "demo"
>("admin");
const onTabChange = (value: "admin" | "attribute" | "terminal" | "demo") => {
setSelectedTab(value);
};
- Add a new button or tab UI element to switch to the
DemoTab
.