Skip to content

Commit

Permalink
[MIRROR] Deprecates inferno state hook [MDB IGNORE] (#1098)
Browse files Browse the repository at this point in the history
* Deprecates inferno state hook

* Modular

---------

Co-authored-by: SkyratBot <[email protected]>
Co-authored-by: Jeremiah <[email protected]>
Co-authored-by: Giz <[email protected]>
  • Loading branch information
4 people authored Dec 13, 2023
1 parent 16fd0d5 commit 92c6a28
Show file tree
Hide file tree
Showing 75 changed files with 563 additions and 485 deletions.
30 changes: 30 additions & 0 deletions tgui/docs/state-usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Managing component state

React has excellent documentation on useState and useEffect. These hooks should be the ways to manage state in TGUI (v5).
[React Hooks](https://react.dev/learn/state-a-components-memory)

You might find usages of useLocalState. This should be considered deprecated and will be removed in the future. In older versions of TGUI, InfernoJS did not have hooks, so these were used to manage state. useSharedState is still used in some places where uis are considered "IC" and user input is shared with all persons at the console/machine/thing.

## A Note on State

Many beginners tend to overuse state (or hooks all together). State is effective when you want to implement user interactivity, or are handling asynchronous data, but if you are simply using state to store a value that is not changing, you should consider using a variable instead.

In previous versions of React, each setState would trigger a re-render, which would cause poorly written components to cascade re-render on each page load. Messy! Though this is no longer the case with batch rendering, it's still worthwhile to point out that you might be overusing it.

## Derived state

One great way to cut back on state usage is by using props or other state as the basis for a variable. You'll see many examples of this in the TGUI codebase. What does this mean? Here's an example:

```tsx
// Bad
const [count, setCount] = useState(0);
const [isEven, setIsEven] = useState(false);

useEffect(() => {
setIsEven(count % 2 === 0);
}, [count]);

// Good!
const [count, setCount] = useState(0);
const isEven = count % 2 === 0; // Derived state
```
1 change: 1 addition & 0 deletions tgui/packages/tgui/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ type StateWithSetter<T> = [T, (nextState: T) => void];
* @param context React context.
* @param key Key which uniquely identifies this state in Redux store.
* @param initialState Initializes your global variable with this value.
* @deprecated Use useState and useEffect when you can. Pass the state as a prop.
*/
export const useLocalState = <T>(
key: string,
Expand Down
1 change: 1 addition & 0 deletions tgui/packages/tgui/interfaces/AbductorConsole.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { GenericUplink } from './Uplink/GenericUplink';

export const AbductorConsole = (props) => {
const [tab, setTab] = useSharedState('tab', 1);

return (
<Window theme="abductor" width={600} height={532}>
<Window.Content scrollable>
Expand Down
4 changes: 2 additions & 2 deletions tgui/packages/tgui/interfaces/AccountingConsole.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import {
Tabs,
} from '../components';
import { useBackend } from '../backend';
import { useLocalState } from '../backend';
import { Window } from '../layouts';
import { BooleanLike } from 'common/react';
import { useState } from 'react';

type Data = {
PlayerAccounts: PlayerAccount[];
Expand Down Expand Up @@ -38,7 +38,7 @@ enum SCREENS {
}

export const AccountingConsole = (props) => {
const [screenmode, setScreenmode] = useLocalState('tab_main', SCREENS.users);
const [screenmode, setScreenmode] = useState(SCREENS.users);

return (
<Window width={300} height={360}>
Expand Down
3 changes: 2 additions & 1 deletion tgui/packages/tgui/interfaces/Achievements.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useBackend, useLocalState } from '../backend';
import { Box, Flex, Icon, Table, Tabs, Tooltip } from '../components';
import { Window } from '../layouts';
import { useState } from 'react';

export const Achievements = (props) => {
const { data } = useBackend();
Expand Down Expand Up @@ -94,7 +95,7 @@ const Achievement = (props) => {
const HighScoreTable = (props) => {
const { data } = useBackend();
const { highscore: highscores, user_ckey } = data;
const [highScoreIndex, setHighScoreIndex] = useLocalState('highscore', 0);
const [highScoreIndex, setHighScoreIndex] = useState(0);
const highscore = highscores[highScoreIndex];
if (!highscore) {
return null;
Expand Down
15 changes: 5 additions & 10 deletions tgui/packages/tgui/interfaces/Adminhelp.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { BooleanLike } from 'common/react';
import { useBackend, useLocalState } from '../backend';
import { useState } from 'react';
import { useBackend } from '../backend';
import { TextArea, Stack, Button, NoticeBox, Input, Box } from '../components';
import { Window } from '../layouts';

Expand All @@ -18,15 +19,9 @@ export const Adminhelp = (props) => {
bannedFromUrgentAhelp,
urgentAhelpPromptMessage,
} = data;
const [requestForAdmin, setRequestForAdmin] = useLocalState(
'request_for_admin',
false,
);
const [currentlyInputting, setCurrentlyInputting] = useLocalState(
'confirm_request',
false,
);
const [ahelpMessage, setAhelpMessage] = useLocalState('ahelp_message', '');
const [requestForAdmin, setRequestForAdmin] = useState(false);
const [currentlyInputting, setCurrentlyInputting] = useState(false);
const [ahelpMessage, setAhelpMessage] = useState('');

const confirmationText = 'alert admins';
return (
Expand Down
5 changes: 3 additions & 2 deletions tgui/packages/tgui/interfaces/AmmoWorkbench.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// THIS IS A SKYRAT UI FILE
import { toTitleCase } from 'common/string';
import { useBackend, useSharedState, useLocalState } from '../backend';
import { useBackend, useSharedState } from '../backend';
import {
Box,
Button,
Expand All @@ -16,6 +16,7 @@ import {
Tooltip,
} from '../components';
import { Window } from '../layouts';
import { useState } from 'react';

export const AmmoWorkbench = (props) => {
const [tab, setTab] = useSharedState('tab', 1);
Expand Down Expand Up @@ -245,7 +246,7 @@ export const DatadiskTab = (props) => {
const MaterialRow = (props) => {
const { material, onRelease } = props;

const [amount, setAmount] = useLocalState('amount' + material.name, 1);
const [amount, setAmount] = useState(1);

const amountAvailable = Math.floor(material.amount);
return (
Expand Down
2 changes: 1 addition & 1 deletion tgui/packages/tgui/interfaces/AnomalyRefinery.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { Window } from '../layouts';
import { GasmixParser } from './common/GasmixParser';

export const AnomalyRefinery = (props) => {
const { act, data } = useBackend();
return (
<Window title="Anomaly Refinery" width={550} height={350}>
<Window.Content>
Expand All @@ -26,6 +25,7 @@ const AnomalyRefineryContent = (props) => {
const { act, data } = useBackend();
const [currentTab, changeTab] = useSharedState('exploderTab', 1);
const { core, valvePresent, active } = data;

return (
<Stack vertical fill>
{currentTab === 1 && <CoreCompressorContent />}
Expand Down
5 changes: 3 additions & 2 deletions tgui/packages/tgui/interfaces/AntagInfoAssaultops.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// THIS IS A SKYRAT UI FILE
import { useBackend, useLocalState } from '../backend';
import { useBackend } from '../backend';
import {
LabeledList,
Stack,
Expand All @@ -13,6 +13,7 @@ import {
import { BooleanLike } from 'common/react';
import { Window } from '../layouts';
import { Rules } from './AntagInfoRules';
import { useState } from 'react';

type Objectives = {
count: number;
Expand Down Expand Up @@ -51,7 +52,7 @@ type Info = {
};

export const AntagInfoAssaultops = (props) => {
const [tab, setTab] = useLocalState('tab', 1);
const [tab, setTab] = useState(1);
const { data } = useBackend<Info>();
const { required_keys, uploaded_keys, objectives } = data;
return (
Expand Down
7 changes: 4 additions & 3 deletions tgui/packages/tgui/interfaces/AntagInfoChangeling.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BooleanLike } from 'common/react';
import { multiline } from 'common/string';
import { useBackend, useSharedState } from '../backend';
import { useState } from 'react';
import { useBackend } from '../backend';
import {
Button,
Dimmer,
Expand Down Expand Up @@ -220,15 +221,15 @@ const AbilitiesSection = (props) => {
const MemoriesSection = (props) => {
const { data } = useBackend<Info>();
const { memories } = data;
const [selectedMemory, setSelectedMemory] = useSharedState(
'memory',
const [selectedMemory, setSelectedMemory] = useState(
(!!memories && memories[0]) || null,
);
const memoryMap = {};
for (const index in memories) {
const memory = memories[index];
memoryMap[memory.name] = memory;
}

return (
<Section
fill
Expand Down
5 changes: 3 additions & 2 deletions tgui/packages/tgui/interfaces/AntagInfoHeretic.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useBackend, useLocalState } from '../backend';
import { useBackend } from '../backend';
import { Section, Stack, Box, Tabs, Button, BlockQuote } from '../components';
import { Window } from '../layouts';
import { BooleanLike } from 'common/react';
Expand All @@ -7,6 +7,7 @@ import {
Objective,
ReplaceObjectivesButton,
} from './common/Objectives';
import { useState } from 'react';
// SKYRAT EDIT BEGIN
import { Rules } from './AntagInfoRules';
// SKYRAT EDIT END
Expand Down Expand Up @@ -317,7 +318,7 @@ export const AntagInfoHeretic = (props) => {
const { data } = useBackend<Info>();
const { ascended } = data;

const [currentTab, setTab] = useLocalState('currentTab', 0);
const [currentTab, setTab] = useState(0);

return (
<Window width={675} height={635}>
Expand Down
5 changes: 3 additions & 2 deletions tgui/packages/tgui/interfaces/AntagInfoMalf.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useBackend, useLocalState } from '../backend';
import { useBackend } from '../backend';
import { multiline } from 'common/string';
import { GenericUplink, Item } from './Uplink/GenericUplink';
import { BlockQuote, Button, Section, Stack, Tabs } from '../components';
Expand All @@ -9,6 +9,7 @@ import {
Objective,
ReplaceObjectivesButton,
} from './common/Objectives';
import { useState } from 'react';
// SKYRAT EDIT BEGIN
import { Rules } from './AntagInfoRules';
// SKYRAT EDIT END
Expand Down Expand Up @@ -178,7 +179,7 @@ const CodewordsSection = (props) => {
export const AntagInfoMalf = (props) => {
const { act, data } = useBackend<Info>();
const { processingTime, categories } = data;
const [antagInfoTab, setAntagInfoTab] = useLocalState('antagInfoTab', 0);
const [antagInfoTab, setAntagInfoTab] = useState(0);
const categoriesList: string[] = [];
const items: Item[] = [];
for (let i = 0; i < categories.length; i++) {
Expand Down
5 changes: 3 additions & 2 deletions tgui/packages/tgui/interfaces/AtmosControlConsole.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useBackend, useLocalState } from '../backend';
import { useState } from 'react';
import { useBackend } from '../backend';
import {
Box,
Button,
Expand Down Expand Up @@ -32,7 +33,7 @@ export const AtmosControlConsole = (props) => {
control: boolean;
}>();
const chambers = data.chambers || [];
const [chamberId, setChamberId] = useLocalState('chamberId', chambers[0]?.id);
const [chamberId, setChamberId] = useState(chambers[0]?.id);
const selectedChamber =
chambers.length === 1
? chambers[0]
Expand Down
14 changes: 10 additions & 4 deletions tgui/packages/tgui/interfaces/AuxBaseConsole.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { BooleanLike } from 'common/react';
import { useBackend, useLocalState } from '../backend';
import { useState } from 'react';
import { useBackend } from '../backend';
import { Button, NoticeBox, Section, Table, Tabs } from '../components';
import { Window } from '../layouts';
import { ShuttleConsoleContent } from './ShuttleConsole';
Expand Down Expand Up @@ -27,9 +28,14 @@ const STATUS_COLOR_KEYS = {
'All Clear': 'good',
} as const;

enum TAB {
Shuttle = 1,
Aux,
}

export const AuxBaseConsole = (props) => {
const { data } = useBackend<Data>();
const [tab, setTab] = useLocalState('tab', 1);
const [tab, setTab] = useState(TAB.Shuttle);
const { type, blind_drop, turrets = [] } = data;

return (
Expand All @@ -56,10 +62,10 @@ export const AuxBaseConsole = (props) => {
Turrets ({turrets.length})
</Tabs.Tab>
</Tabs>
{tab === 1 && (
{tab === TAB.Shuttle && (
<ShuttleConsoleContent type={type} blind_drop={blind_drop} />
)}
{tab === 2 && <AuxBaseConsoleContent />}
{tab === TAB.Aux && <AuxBaseConsoleContent />}
</Window.Content>
</Window>
);
Expand Down
22 changes: 14 additions & 8 deletions tgui/packages/tgui/interfaces/BluespaceLocator.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useBackend, useLocalState } from '../backend';
import { useState } from 'react';
import { useBackend } from '../backend';
import { Icon, ProgressBar, Tabs } from '../components';
import { Window } from '../layouts';

Expand All @@ -25,28 +26,33 @@ const DIRECTION_TO_ICON = {
northwest: 315,
} as const;

enum TAB {
Implant,
Beacon,
}

export const BluespaceLocator = (props) => {
const [tab, setTab] = useLocalState('tab', 'implant');
const [tab, setTab] = useState(TAB.Implant);

return (
<Window width={300} height={300}>
<Window.Content scrollable>
<Tabs>
<Tabs.Tab
selected={tab === 'implant'}
onClick={() => setTab('implant')}
selected={tab === TAB.Implant}
onClick={() => setTab(TAB.Implant)}
>
Implants
</Tabs.Tab>
<Tabs.Tab
selected={tab === 'beacon'}
onClick={() => setTab('beacon')}
selected={tab === TAB.Beacon}
onClick={() => setTab(TAB.Beacon)}
>
Teleporter Beacons
</Tabs.Tab>
</Tabs>
{(tab === 'beacon' && <TeleporterBeacons />) ||
(tab === 'implant' && <TrackingImplants />)}
{(TAB.Beacon && <TeleporterBeacons />) ||
(TAB.Implant && <TrackingImplants />)}
</Window.Content>
</Window>
);
Expand Down
15 changes: 9 additions & 6 deletions tgui/packages/tgui/interfaces/CameraConsole.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { filter, sortBy } from 'common/collections';
import { flow } from 'common/fp';
import { BooleanLike, classes } from 'common/react';
import { createSearch } from 'common/string';
import { useBackend, useLocalState } from '../backend';
import { useState } from 'react';
import { useBackend } from '../backend';
import {
Button,
ByondUi,
Expand Down Expand Up @@ -88,21 +89,23 @@ export const CameraConsole = (props) => {
};

export const CameraContent = (props) => {
const [searchText, setSearchText] = useState('');

return (
<Stack fill>
<Stack.Item grow>
<CameraSelector />
<CameraSelector searchText={searchText} setSearchText={setSearchText} />
</Stack.Item>
<Stack.Item grow={3}>
<CameraControls />
<CameraControls searchText={searchText} />
</Stack.Item>
</Stack>
);
};

const CameraSelector = (props) => {
const { act, data } = useBackend<Data>();
const [searchText, setSearchText] = useLocalState('searchText', '');
const { searchText, setSearchText } = props;
const { activeCamera } = data;
const cameras = selectCameras(data.cameras, searchText);

Expand Down Expand Up @@ -149,10 +152,10 @@ const CameraSelector = (props) => {
);
};

const CameraControls = (props) => {
const CameraControls = (props: { searchText: string }) => {
const { act, data } = useBackend<Data>();
const { activeCamera, can_spy, mapRef } = data;
const [searchText] = useLocalState('searchText', '');
const { searchText } = props;

const cameras = selectCameras(data.cameras, searchText);

Expand Down
Loading

0 comments on commit 92c6a28

Please sign in to comment.