Skip to main content

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:

  1. Import DemoTab at the top of your InspectorModule.tsx:
import DemoTab from "./tabs/DemoTab";
  1. Add a new case for DemoTab in the switch 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;
}
  1. Update the useState and onTabChange to include the new 'demo' tab:
const [selectedTab, setSelectedTab] = useState<
"admin" | "attribute" | "terminal" | "demo"
>("admin");

const onTabChange = (value: "admin" | "attribute" | "terminal" | "demo") => {
setSelectedTab(value);
};
  1. Add a new button or tab UI element to switch to the DemoTab.