CODEBASE: Add custom useRerender hook (#359)

This commit is contained in:
Snarling 2023-02-11 13:22:55 -05:00 committed by GitHub
parent b4074328ec
commit 6a6043c509
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 137 additions and 357 deletions

@ -2,7 +2,7 @@
* Root React component for the Augmentations UI page that display all of your
* owned and purchased Augmentations and Source-Files.
*/
import React, { useState, useEffect } from "react";
import React, { useState } from "react";
import { InstalledAugmentations } from "./InstalledAugmentations";
import { PlayerMultipliers } from "./PlayerMultipliers";
@ -26,6 +26,7 @@ import { formatNumberNoSuffix } from "../../ui/formatNumber";
import { Info } from "@mui/icons-material";
import { Link } from "@mui/material";
import { AlertEvents } from "../../ui/React/AlertManager";
import { useRerender } from "../../ui/React/hooks";
const NeuroFluxDisplay = (): React.ReactElement => {
const level = Player.augmentations.find((e) => e.name === AugmentationNames.NeuroFluxGovernor)?.level ?? 0;
@ -84,14 +85,7 @@ interface IProps {
export function AugmentationsRoot(props: IProps): React.ReactElement {
const [installOpen, setInstallOpen] = useState(false);
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((o) => !o);
}
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
const rerender = useRerender(200);
function doExport(): void {
props.exportGameFn();

@ -15,9 +15,10 @@ import { Settings } from "../../Settings/Settings";
import { Player } from "@player";
import { StaticAugmentations } from "../StaticAugmentations";
import { AugmentationNames } from "../data/AugmentationNames";
import { useRerender } from "../../ui/React/hooks";
export function InstalledAugmentations(): React.ReactElement {
const setRerender = useState(true)[1];
const rerender = useRerender();
const sourceAugs = Player.augmentations.slice().filter((aug) => aug.name !== AugmentationNames.NeuroFluxGovernor);
const [selectedAug, setSelectedAug] = useState(sourceAugs[0]);
@ -28,10 +29,6 @@ export function InstalledAugmentations(): React.ReactElement {
});
}
function rerender(): void {
setRerender((old) => !old);
}
function sortByAcquirementTime(): void {
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.AcquirementTime;
rerender();

@ -1,4 +1,4 @@
import React, { useState } from "react";
import React from "react";
import { formatNumberNoSuffix } from "../../ui/formatNumber";
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { ActionTypes } from "../data/ActionTypes";
@ -14,6 +14,7 @@ import { StartButton } from "./StartButton";
import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper";
import { useRerender } from "../../ui/React/hooks";
interface IProps {
bladeburner: Bladeburner;
@ -21,10 +22,7 @@ interface IProps {
}
export function BlackOpElem(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
const rerender = useRerender();
const isCompleted = props.bladeburner.blackops[props.action.name] != null;
if (isCompleted) {
return (

@ -1,22 +1,14 @@
import React, { useState, useEffect } from "react";
import React from "react";
import { Stats } from "./Stats";
import { Console } from "./Console";
import { AllPages } from "./AllPages";
import { Player } from "@player";
import Box from "@mui/material/Box";
import { useRerender } from "../../ui/React/hooks";
export function BladeburnerRoot(): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
useRerender(200);
const bladeburner = Player.bladeburner;
if (!bladeburner) return <></>;
return (

@ -11,6 +11,7 @@ import TextField from "@mui/material/TextField";
import { Theme } from "@mui/material/styles";
import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
import { useRerender } from "../../ui/React/hooks";
interface ILineProps {
content: React.ReactNode;
@ -54,8 +55,8 @@ interface IProps {
export function Console(props: IProps): React.ReactElement {
const classes = useStyles();
const [command, setCommand] = useState("");
const setRerender = useState(false)[1];
const consoleInput = useRef<HTMLInputElement>(null);
useRerender(1000);
function handleCommandChange(event: React.ChangeEvent<HTMLInputElement>): void {
setCommand(event.target.value);
@ -63,17 +64,6 @@ export function Console(props: IProps): React.ReactElement {
const [consoleHistoryIndex, setConsoleHistoryIndex] = useState(props.bladeburner.consoleHistory.length);
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, 1000);
return () => {
clearInterval(id);
};
}, []);
function handleKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
if (event.key === KEY.ENTER) {
event.preventDefault();

@ -1,4 +1,4 @@
import React, { useState } from "react";
import React from "react";
import { ActionTypes } from "../data/ActionTypes";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
@ -14,6 +14,7 @@ import { StartButton } from "./StartButton";
import { formatNumberNoSuffix, formatBigNumber } from "../../ui/formatNumber";
import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper";
import { useRerender } from "../../ui/React/hooks";
interface IProps {
bladeburner: Bladeburner;
@ -21,10 +22,7 @@ interface IProps {
}
export function ContractElem(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
const rerender = useRerender();
const isActive =
props.bladeburner.action.type === ActionTypes["Contract"] && props.action.name === props.bladeburner.action.name;
const computedActionTimeCurrent = Math.min(

@ -1,4 +1,4 @@
import React, { useState } from "react";
import React from "react";
import { ActionTypes } from "../data/ActionTypes";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
import { formatNumberNoSuffix } from "../../ui/formatNumber";
@ -14,6 +14,7 @@ import { StartButton } from "./StartButton";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
import Paper from "@mui/material/Paper";
import { useRerender } from "../../ui/React/hooks";
interface IProps {
bladeburner: Bladeburner;
@ -21,10 +22,7 @@ interface IProps {
}
export function GeneralActionElem(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
const rerender = useRerender();
const isActive = props.action.name === props.bladeburner.action.name;
const computedActionTimeCurrent = Math.min(
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,

@ -1,4 +1,4 @@
import React, { useState } from "react";
import React from "react";
import { ActionTypes } from "../data/ActionTypes";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
@ -15,6 +15,7 @@ import { CopyableText } from "../../ui/React/CopyableText";
import { formatNumberNoSuffix, formatBigNumber } from "../../ui/formatNumber";
import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper";
import { useRerender } from "../../ui/React/hooks";
interface IProps {
bladeburner: Bladeburner;
@ -22,10 +23,7 @@ interface IProps {
}
export function OperationElem(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
const rerender = useRerender();
const isActive =
props.bladeburner.action.type === ActionTypes["Operation"] && props.action.name === props.bladeburner.action.name;
const computedActionTimeCurrent = Math.min(

@ -1,7 +1,7 @@
// React Components for the Corporation UI's navigation tabs
// These are the tabs at the top of the UI that let you switch to different
// divisions, see an overview of your corporation, or create a new industry
import React, { useState, useEffect } from "react";
import React, { useState } from "react";
import { MainPanel } from "./MainPanel";
import { IndustryType } from "../data/Enums";
import { ExpandIndustryTab } from "./ExpandIndustryTab";
@ -11,22 +11,16 @@ import { Overview } from "./Overview";
import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import { useRerender } from "../../ui/React/hooks";
export function CorporationRoot(): React.ReactElement {
const rerender = useRerender(200);
const corporation = Player.corporation;
if (corporation === null) return <></>;
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
const [divisionName, setDivisionName] = useState<string | number>("Overview");
function handleChange(event: React.SyntheticEvent, tab: string | number): void {
setDivisionName(tab);
}
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
const canExpand =
Object.values(IndustryType).filter(

@ -14,6 +14,7 @@ import Box from "@mui/material/Box";
import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import { CityName } from "../../../Enums";
import { useRerender } from "../../../ui/React/hooks";
interface IProps {
open: boolean;
@ -32,11 +33,7 @@ export function ExportModal(props: IProps): React.ReactElement {
const [industry, setIndustry] = useState<string>(defaultDivision.name);
const [city, setCity] = useState(Object.keys(defaultDivision.warehouses)[0] as CityName);
const [amt, setAmt] = useState("");
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
const rerender = useRerender();
function onCityChange(event: SelectChangeEvent<string>): void {
setCity(event.target.value as CityName);

@ -8,6 +8,7 @@ import TextField from "@mui/material/TextField";
import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch";
import Tooltip from "@mui/material/Tooltip";
import { useRerender } from "../../../ui/React/hooks";
interface IMarketTA2Props {
mat: Material;
@ -17,10 +18,7 @@ function MarketTA2(props: IMarketTA2Props): React.ReactElement {
const division = useDivision();
if (!division.hasResearch("Market-TA.II")) return <></>;
const [newCost, setNewCost] = useState<number>(props.mat.bCost);
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
const rerender = useRerender();
const markupLimit = props.mat.getMarkupLimit();
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {

@ -8,6 +8,7 @@ import TextField from "@mui/material/TextField";
import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch";
import Tooltip from "@mui/material/Tooltip";
import { useRerender } from "../../../ui/React/hooks";
interface ITa2Props {
product: Product;
@ -18,10 +19,7 @@ function MarketTA2(props: ITa2Props): React.ReactElement {
if (!division.hasResearch("Market-TA.II")) return <></>;
const markupLimit = props.product.rat / props.product.mku;
const [value, setValue] = useState(props.product.pCost);
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
const rerender = useRerender();
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
setValue(parseFloat(event.target.value));

@ -10,6 +10,7 @@ import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch";
import { CorpMaterialName } from "@nsdefs";
import { materialNames } from "../../data/Constants";
import { useRerender } from "../../../ui/React/hooks";
interface ILeftoverProps {
matName: CorpMaterialName;
@ -49,10 +50,7 @@ interface IProps {
export function SmartSupplyModal(props: IProps): React.ReactElement {
const division = useDivision();
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
const rerender = useRerender();
// Smart Supply Checkbox
function smartSupplyOnChange(e: React.ChangeEvent<HTMLInputElement>): void {

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React, { useEffect } from "react";
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { CONSTANTS } from "../../Constants";
import { StaneksGiftEvents } from "../StaneksGiftEvents";
@ -11,16 +11,14 @@ import { ActiveFragment } from "../ActiveFragment";
import { Fragments } from "../Fragment";
import { DummyGrid } from "./DummyGrid";
import Container from "@mui/material/Container";
import { useRerender } from "../../ui/React/hooks";
type IProps = {
staneksGift: StaneksGift;
};
export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement {
const setRerender = useState(true)[1];
function rerender(): void {
setRerender((o) => !o);
}
const rerender = useRerender();
useEffect(() => StaneksGiftEvents.subscribe(rerender), []);
return (
<Container maxWidth="lg" disableGutters sx={{ mx: 0 }}>

@ -1,5 +1,5 @@
import { Box, Button, Tooltip, Typography, Paper, Container } from "@mui/material";
import React, { useState } from "react";
import React from "react";
import { StaticAugmentations } from "../../Augmentation/StaticAugmentations";
import { getGenericAugmentationPriceMultiplier } from "../../Augmentation/AugmentationHelpers";
@ -15,6 +15,7 @@ import { FactionNames } from "../data/FactionNames";
import { Faction } from "../Faction";
import { getFactionAugmentationsFiltered, hasAugmentationPrereqs, purchaseAugmentation } from "../FactionHelpers";
import { CONSTANTS } from "../../Constants";
import { useRerender } from "../../ui/React/hooks";
type IProps = {
faction: Faction;
@ -23,11 +24,7 @@ type IProps = {
/** Root React Component for displaying a faction's "Purchase Augmentations" page */
export function AugmentationsPage(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
const rerender = useRerender();
function getAugs(): string[] {
return getFactionAugmentationsFiltered(props.faction);

@ -3,7 +3,7 @@
* This is the component for displaying a single faction's UI, not the list of all
* accessible factions
*/
import React, { useState, useEffect } from "react";
import React, { useState } from "react";
import { AugmentationsPage } from "./AugmentationsPage";
import { DonateOption } from "./DonateOption";
@ -25,6 +25,7 @@ import { FactionNames } from "../data/FactionNames";
import { GangButton } from "./GangButton";
import { FactionWork } from "../../Work/FactionWork";
import { FactionWorkType } from "../../Enums";
import { useRerender } from "../../ui/React/hooks";
type IProps = {
faction: Faction;
@ -152,18 +153,9 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
}
export function FactionRoot(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
const rerender = useRerender(200);
const [purchasingAugs, setPurchasingAugs] = useState(props.augPage);
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
const faction = props.faction;
if (!Player.factions.includes(faction.name)) {

@ -1,6 +1,6 @@
import { Explore, Info, LastPage, LocalPolice, NewReleases, Report, SportsMma } from "@mui/icons-material";
import { Box, Button, Container, Paper, Tooltip, Typography, useTheme } from "@mui/material";
import React, { useEffect, useState } from "react";
import React, { useEffect } from "react";
import { Player } from "@player";
import { Settings } from "../../Settings/Settings";
import { formatFavor, formatReputation } from "../../ui/formatNumber";
@ -9,6 +9,7 @@ import { FactionNames } from "../data/FactionNames";
import { Faction } from "../Faction";
import { getFactionAugmentationsFiltered, joinFaction } from "../FactionHelpers";
import { Factions } from "../Factions";
import { useRerender } from "../../ui/React/hooks";
export const InvitationsSeen: string[] = [];
@ -166,15 +167,7 @@ const FactionElement = (props: FactionElementProps): React.ReactElement => {
export function FactionsRoot(): React.ReactElement {
const theme = useTheme();
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
const rerender = useRerender(200);
useEffect(() => {
Player.factionInvitations.forEach((faction) => {
if (InvitationsSeen.includes(faction)) return;

@ -2,7 +2,7 @@
* React component for general information about the faction. This includes the
* factions "motto", reputation, favor, and gameplay instructions
*/
import React, { useState, useEffect } from "react";
import React from "react";
import { Faction } from "../Faction";
import { FactionInfo } from "../FactionInfo";
@ -16,6 +16,7 @@ import createStyles from "@mui/styles/createStyles";
import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip";
import Box from "@mui/material/Box";
import { useRerender } from "../../ui/React/hooks";
type IProps = {
faction: Faction;
@ -43,16 +44,7 @@ function DefaultAssignment(): React.ReactElement {
}
export function Info(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
useRerender(200);
const classes = useStyles();
const Assignment = props.factionInfo.assignment ?? DefaultAssignment;

@ -19,6 +19,7 @@ import { UpgradeType } from "../data/upgrades";
import { Player } from "@player";
import { Settings } from "../../Settings/Settings";
import { StatsRow } from "../../ui/React/StatsRow";
import { useRerender } from "../../ui/React/hooks";
interface INextRevealProps {
upgrades: string[];
@ -86,13 +87,9 @@ interface IPanelProps {
function GangMemberUpgradePanel(props: IPanelProps): React.ReactElement {
const gang = useGang();
const setRerender = useState(false)[1];
const rerender = useRerender();
const [currentCategory, setCurrentCategory] = useState("Weapons");
function rerender(): void {
setRerender((old) => !old);
}
function filterUpgrades(list: string[], type: UpgradeType): GangMemberUpgrade[] {
return Object.keys(GangMemberUpgrades)
.filter((upgName: string) => {

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React, { useState } from "react";
import { GeneralInfo } from "./GeneralInfo";
import { HacknetNodeElem } from "./HacknetNodeElem";
@ -25,21 +25,14 @@ import Typography from "@mui/material/Typography";
import Grid from "@mui/material/Grid";
import Button from "@mui/material/Button";
import { Box } from "@mui/material";
import { useRerender } from "../../ui/React/hooks";
/** Root React Component for the Hacknet Node UI */
export function HacknetRoot(): React.ReactElement {
const [open, setOpen] = useState(false);
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
const rerender = useRerender(200);
const [purchaseMultiplier, setPurchaseMultiplier] = useState<number | "MAX">(PurchaseMultipliers.x1);
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
let totalProduction = 0;
for (let i = 0; i < Player.hacknetNodes.length; ++i) {
const node = Player.hacknetNodes[i];

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React from "react";
import { HashUpgrades } from "../HashUpgrades";
@ -7,6 +7,7 @@ import { HacknetUpgradeElem } from "./HacknetUpgradeElem";
import { Modal } from "../../ui/React/Modal";
import { Player } from "@player";
import Typography from "@mui/material/Typography";
import { useRerender } from "../../ui/React/hooks";
interface IProps {
open: boolean;
@ -15,15 +16,7 @@ interface IProps {
/** Create the pop-up for purchasing upgrades with hashes */
export function HashUpgradeModal(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(() => setRerender((old) => !old), 200);
return () => clearInterval(id);
}, []);
const rerender = useRerender(200);
const hashManager = Player.hashManager;
if (!hashManager) {

@ -3,7 +3,7 @@
*
* This subcomponent renders all of the buttons for applying to jobs at a company
*/
import React, { useState, useEffect } from "react";
import React, { useState } from "react";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import Tooltip from "@mui/material/Tooltip";
@ -25,6 +25,7 @@ import { Page } from "../../ui/Router";
import { Player } from "@player";
import { QuitJobModal } from "../../Company/ui/QuitJobModal";
import { CompanyWork } from "../../Work/CompanyWork";
import { useRerender } from "../../ui/React/hooks";
type IProps = {
locName: LocationName;
@ -32,15 +33,8 @@ type IProps = {
export function CompanyLocation(props: IProps): React.ReactElement {
const [quitOpen, setQuitOpen] = useState(false);
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
const rerender = useRerender(200);
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
/**
* We'll keep a reference to the Company that this component is being rendered for,
* so we don't have to look it up every time

@ -12,20 +12,12 @@ import { getHospitalizationCost } from "../../Hospital/Hospital";
import { Money } from "../../ui/React/Money";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { useEffect, useState } from "react";
import { useRerender } from "../../ui/React/hooks";
export function HospitalLocation(): React.ReactElement {
/** Stores button styling that sets them all to block display */
const btnStyle = { display: "block" };
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
const rerender = useRerender(200);
function getHealed(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {

@ -14,7 +14,6 @@ interface IProps {
onClose: () => void;
ram: number;
cost: number;
rerender: () => void;
}
/** React Component for the popup used to purchase a new server. */

@ -3,7 +3,7 @@
*
* This subcomponent renders all of the buttons for committing crimes
*/
import React, { useState, useEffect } from "react";
import React from "react";
import Button from "@mui/material/Button";
import Tooltip from "@mui/material/Tooltip";
@ -15,15 +15,11 @@ import { Page } from "../../ui/Router";
import { Player } from "@player";
import { Box } from "@mui/material";
import { Crime } from "../../Crime/Crime";
import { useRerender } from "../../ui/React/hooks";
export function SlumsLocation(): React.ReactElement {
const setRerender = useState(false)[1];
const rerender = () => setRerender((o) => !o);
useRerender(1000);
const crimes = Object.values(Crimes);
useEffect(() => {
const timerId = setInterval(() => rerender(), 1000);
return () => clearInterval(timerId);
});
function doCrime(e: React.MouseEvent<HTMLElement>, crime: Crime) {
if (!e.isTrusted) return;

@ -3,7 +3,7 @@
*
* This subcomponent renders all of the buttons for purchasing things from tech vendors
*/
import React, { useState, useEffect } from "react";
import React, { useState } from "react";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
@ -19,13 +19,9 @@ import { Player } from "@player";
import { PurchaseServerModal } from "./PurchaseServerModal";
import { formatRam } from "../../ui/formatNumber";
import { Box } from "@mui/material";
import { useRerender } from "../../ui/React/hooks";
interface IServerProps {
ram: number;
rerender: () => void;
}
function ServerButton(props: IServerProps): React.ReactElement {
function ServerButton(props: { ram: number }): React.ReactElement {
const [open, setOpen] = useState(false);
const cost = getPurchaseServerCost(props.ram);
return (
@ -34,35 +30,17 @@ function ServerButton(props: IServerProps): React.ReactElement {
Purchase {formatRam(props.ram)} Server&nbsp;-&nbsp;
<Money money={cost} forPurchase={true} />
</Button>
<PurchaseServerModal
open={open}
onClose={() => setOpen(false)}
ram={props.ram}
cost={cost}
rerender={props.rerender}
/>
<PurchaseServerModal open={open} onClose={() => setOpen(false)} ram={props.ram} cost={cost} />
</>
);
}
type IProps = {
loc: Location;
};
export function TechVendorLocation(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, 1000);
return () => clearInterval(id);
}, []);
export function TechVendorLocation(props: { loc: Location }): React.ReactElement {
const rerender = useRerender(1000);
const purchaseServerButtons: React.ReactNode[] = [];
for (let i = props.loc.techVendorMinRam; i <= props.loc.techVendorMaxRam; i *= 2) {
purchaseServerButtons.push(<ServerButton key={i} ram={i} rerender={rerender} />);
purchaseServerButtons.push(<ServerButton key={i} ram={i} />);
}
return (

@ -3,7 +3,7 @@
*
* TThis subcomponent renders all of the buttons for traveling to different cities
*/
import React, { useState, useEffect } from "react";
import React, { useState } from "react";
import { CityName } from "../../Enums";
import { TravelConfirmationModal } from "./TravelConfirmationModal";
@ -21,6 +21,7 @@ import { dialogBoxCreate } from "../../ui/React/DialogBox";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import { useRerender } from "../../ui/React/hooks";
function travel(to: CityName): void {
const cost = CONSTANTS.TravelCost;
@ -35,17 +36,9 @@ function travel(to: CityName): void {
}
export function TravelAgencyRoot(): React.ReactElement {
const setRerender = useState(false)[1];
const [open, setOpen] = useState(false);
const [destination, setDestination] = useState(CityName.Sector12);
function rerender(): void {
setRerender((o) => !o);
}
useEffect(() => {
const id = setInterval(rerender, 1000);
return () => clearInterval(id);
}, []);
useRerender(1000);
function startTravel(city: CityName): void {
const cost = CONSTANTS.TravelCost;

@ -1,6 +1,6 @@
import { CheckBox, CheckBoxOutlineBlank, Construction } from "@mui/icons-material";
import { Box, Button, Container, List, ListItemButton, Paper, Typography } from "@mui/material";
import React, { useEffect, useState } from "react";
import React, { useState } from "react";
import { GraftingWork } from "../../../Work/GraftingWork";
import { Augmentation } from "../../../Augmentation/Augmentation";
import { AugmentationNames } from "../../../Augmentation/data/AugmentationNames";
@ -20,6 +20,7 @@ import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFun
import { Player } from "@player";
import { GraftableAugmentation } from "../GraftableAugmentation";
import { calculateGraftingTimeWithBonus, getGraftingAvailableAugs } from "../GraftingHelpers";
import { useRerender } from "../../../ui/React/hooks";
export const GraftableAugmentations = (): Record<string, GraftableAugmentation> => {
const gAugs: Record<string, GraftableAugmentation> = {};
@ -65,11 +66,7 @@ export const GraftingRoot = (): React.ReactElement => {
const [selectedAug, setSelectedAug] = useState(getGraftingAvailableAugs()[0]);
const [graftOpen, setGraftOpen] = useState(false);
const selectedAugmentation = StaticAugmentations[selectedAug];
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
const rerender = useRerender(200);
const getAugsSorted = (): string[] => {
const augs = getGraftingAvailableAugs();
@ -86,11 +83,6 @@ export const GraftingRoot = (): React.ReactElement => {
rerender();
};
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
return (
<Container disableGutters maxWidth="lg" sx={{ mx: 0 }}>
<Button onClick={() => Router.toLocation(Locations[LocationName.NewTokyoVitaLife])}>Back</Button>

@ -2,7 +2,7 @@
* Root React component for the popup that lets player purchase Duplicate
* Sleeves and Sleeve-related upgrades from The Covenant
*/
import React, { useState } from "react";
import React from "react";
import { CovenantSleeveMemoryUpgrade } from "./CovenantSleeveMemoryUpgrade";
@ -17,6 +17,7 @@ import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import { FactionNames } from "../../../Faction/data/FactionNames";
import { useRerender } from "../../../ui/React/hooks";
interface IProps {
open: boolean;
@ -24,18 +25,13 @@ interface IProps {
}
export function CovenantPurchasesRoot(props: IProps): React.ReactElement {
const [update, setUpdate] = useState(0);
const rerender = useRerender();
/** Get the cost to purchase a new Duplicate Sleeve */
function purchaseCost(): number {
return Math.pow(10, Player.sleevesFromCovenant) * BaseCostPerSleeve;
}
/** Force a rerender by just changing an arbitrary state value */
function rerender(): void {
setUpdate(update + 1);
}
// Purchasing a new Duplicate Sleeve
let purchaseDisabled = false;
if (!Player.canAfford(purchaseCost())) {

@ -1,9 +1,10 @@
import { Container, Typography, Paper } from "@mui/material";
import React, { useEffect, useState } from "react";
import React from "react";
import { PurchasableAugmentations } from "../../../Augmentation/ui/PurchasableAugmentations";
import { Player } from "@player";
import { Modal } from "../../../ui/React/Modal";
import { Sleeve } from "../Sleeve";
import { useRerender } from "../../../ui/React/hooks";
interface IProps {
open: boolean;
@ -12,15 +13,7 @@ interface IProps {
}
export function SleeveAugmentationsModal(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, 150);
return () => clearInterval(id);
}, []);
const rerender = useRerender(150);
// Array of all owned Augmentations. Names only
const ownedAugNames = props.sleeve.augmentations.map((e) => e.name);

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React, { useState } from "react";
import { Box, Typography, Button, Container } from "@mui/material";
@ -6,18 +6,11 @@ import { Player } from "@player";
import { SleeveElem } from "./SleeveElem";
import { FAQModal } from "./FAQModal";
import { useRerender } from "../../../ui/React/hooks";
export function SleeveRoot(): React.ReactElement {
const [FAQOpen, setFAQOpen] = useState(false);
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
const rerender = useRerender(200);
return (
<>

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React, { useEffect } from "react";
import { find } from "lodash";
import { Box, Typography, Button, Container, Paper } from "@mui/material";
@ -11,14 +11,12 @@ import { Settings } from "../../Settings/Settings";
import { Programs } from "../Programs";
import { CreateProgramWork } from "../../Work/CreateProgramWork";
import { useRerender } from "../../ui/React/hooks";
export const ProgramsSeen: string[] = [];
export function ProgramsRoot(): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
useRerender(200);
const programs = [...Object.values(Programs)]
.filter((prog) => {
@ -42,11 +40,6 @@ export function ProgramsRoot(): React.ReactElement {
});
}, []);
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
const getHackingLevelRemaining = (lvl: number): number => {
return Math.ceil(Math.max(lvl - (Player.skills.hacking + Player.skills.intelligence / 2), 0));
};

@ -45,6 +45,7 @@ import { Modal } from "../../ui/React/Modal";
import libSource from "!!raw-loader!../NetscriptDefinitions.d.ts";
import { TextField, Tooltip } from "@mui/material";
import { useRerender } from "../../ui/React/hooks";
interface IProps {
// Map of filename -> code
@ -106,10 +107,7 @@ let currentScript: OpenScript | null = null;
// Called every time script editor is opened
export function Root(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((o) => !o);
}
const rerender = useRerender();
const editorRef = useRef<IStandaloneCodeEditor | null>(null);
const monacoRef = useRef<Monaco | null>(null);
const vimStatusRef = useRef<HTMLElement>(null);

@ -4,6 +4,7 @@ import IconButton from "@mui/material/IconButton";
import _ from "lodash";
import { Color, ColorPicker } from "material-ui-color";
import React, { useState } from "react";
import { useRerender } from "../../ui/React/hooks";
import { Settings } from "../../Settings/Settings";
import { Modal } from "../../ui/React/Modal";
import { OptionSwitch } from "../../ui/React/OptionSwitch";
@ -65,10 +66,7 @@ function ColorEditor({ label, themePath, onColorChange, color, defaultColor }: I
}
export function ThemeEditorModal(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((o) => !o);
}
const rerender = useRerender();
// Need to deep copy the object since it has nested attributes
const [themeCopy, setThemeCopy] = useState<IScriptEditorTheme>(JSON.parse(JSON.stringify(Settings.EditorTheme)));

@ -54,6 +54,7 @@ import { ProgramsSeen } from "../../Programs/ui/ProgramsRoot";
import { InvitationsSeen } from "../../Faction/ui/FactionsRoot";
import { hash } from "../../hash/hash";
import { Locations } from "../../Locations/Locations";
import { useRerender } from "../../ui/React/hooks";
const RotatedDoubleArrowIcon = React.forwardRef((props: { color: "primary" | "secondary" | "error" }, __ref) => (
<DoubleArrowIcon color={props.color} style={{ transform: "rotate(-90deg)" }} />
@ -108,15 +109,7 @@ interface IProps {
}
export function SidebarRoot(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
useRerender(200);
let flash: Page | null = null;
switch (ITutorial.currStep) {

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React from "react";
import { InfoAndPurchases } from "./InfoAndPurchases";
import { StockTickers } from "./StockTickers";
@ -6,6 +6,7 @@ import { StockTickers } from "./StockTickers";
import { IStockMarket } from "../IStockMarket";
import { Player } from "@player";
import { useRerender } from "../../ui/React/hooks";
type IProps = {
stockMarket: IStockMarket;
@ -13,15 +14,7 @@ type IProps = {
/** Root React component for the Stock Market UI */
export function StockMarketRoot(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
const rerender = useRerender(200);
return (
<>
<InfoAndPurchases rerender={rerender} />

@ -10,13 +10,14 @@ import { StockTickersConfig, TickerDisplayMode } from "./StockTickersConfig";
import { IStockMarket } from "../IStockMarket";
import { Stock } from "../Stock";
import { useRerender } from "../../ui/React/hooks";
type IProps = {
stockMarket: IStockMarket;
};
export function StockTickers(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
const rerender = useRerender();
const [tickerDisplayMode, setTickerDisplayMode] = useState(TickerDisplayMode.AllStocks);
const [watchlistSymbols, setWatchlistSymbols] = useState<string[]>([]);
@ -39,10 +40,6 @@ export function StockTickers(props: IProps): React.ReactElement {
}
}
function rerender(): void {
setRerender((old) => !old);
}
const tickers: React.ReactElement[] = [];
for (const stockMarketProp of Object.keys(props.stockMarket)) {
const val = props.stockMarket[stockMarketProp];

@ -16,6 +16,7 @@ import { CodingContractModal } from "../../ui/React/CodingContractModal";
import _ from "lodash";
import { ANSIITypography } from "../../ui/React/ANSIITypography";
import { useRerender } from "../../ui/React/hooks";
function ActionTimer(): React.ReactElement {
return (
@ -44,11 +45,8 @@ const useStyles = makeStyles((theme: Theme) =>
export function TerminalRoot(): React.ReactElement {
const scrollHook = useRef<HTMLDivElement>(null);
const setRerender = useState(0)[1];
const rerender = useRerender();
const [key, setKey] = useState(0);
function rerender(): void {
setRerender((old) => old + 1);
}
function clear(): void {
setKey((key) => key + 1);

@ -2,30 +2,23 @@
* Root React Component for the "Active Scripts" UI page. This page displays
* and provides information about all of the player's scripts that are currently running
*/
import React, { useState, useEffect } from "react";
import React, { useState } from "react";
import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import { ActiveScriptsPage } from "./ActiveScriptsPage";
import { RecentScriptsPage } from "./RecentScriptsPage";
import { WorkerScript } from "../../Netscript/WorkerScript";
import { useRerender } from "../React/hooks";
interface IProps {
workerScripts: Map<number, WorkerScript>;
}
export function ActiveScriptsRoot(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
const [tab, setTab] = useState<"active" | "recent">("active");
useRerender(200);
function handleChange(event: React.SyntheticEvent, tab: "active" | "recent"): void {
setTab(tab);
}

@ -16,6 +16,7 @@ import { BaseServer } from "../../Server/BaseServer";
import { Settings } from "../../Settings/Settings";
import { TablePaginationActionsAll } from "../React/TablePaginationActionsAll";
import SearchIcon from "@mui/icons-material/Search";
import { useRerender } from "../React/hooks";
// Map of server hostname -> all workerscripts on that server for all active scripts
interface IServerData {
@ -35,7 +36,7 @@ export function ServerAccordions(props: IProps): React.ReactElement {
const [filter, setFilter] = useState("");
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(Settings.ActiveScriptsServerPageSize);
const setRerender = useState(false)[1];
const rerender = useRerender();
const handleChangePage = (event: unknown, newPage: number): void => {
setPage(newPage);
@ -78,10 +79,6 @@ export function ServerAccordions(props: IProps): React.ReactElement {
(data.server.hostname.includes(filter) || data.server.runningScripts.find((s) => s.filename.includes(filter))),
);
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => WorkerScriptStartStopEventEmitter.subscribe(rerender));
return (

@ -1,6 +1,6 @@
import { Paper, Table, TableBody, Box, IconButton, Typography, Container, Tooltip } from "@mui/material";
import { MoreHoriz, Info } from "@mui/icons-material";
import React, { useEffect, useState } from "react";
import React, { useState } from "react";
import { BitNodes, defaultMultipliers, getBitNodeMultipliers } from "../BitNode/BitNode";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { BitNodeMultipliersDisplay } from "../BitNode/ui/BitnodeMultipliersDescription";
@ -16,6 +16,7 @@ import { Money } from "./React/Money";
import { StatsRow } from "./React/StatsRow";
import { StatsTable } from "./React/StatsTable";
import { isEqual } from "lodash";
import { useRerender } from "./React/hooks";
interface EmployersModalProps {
open: boolean;
@ -199,15 +200,7 @@ function MoneyModal({ open, onClose }: IMoneyModalProps): React.ReactElement {
export function CharacterStats(): React.ReactElement {
const [moneyOpen, setMoneyOpen] = useState(false);
const [employersOpen, setEmployersOpen] = useState(false);
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
useRerender(200);
const timeRows = [
["Since last Augmentation installation", convertTimeMsToTimeElapsedString(Player.playtimeSinceLastAug)],

@ -77,6 +77,7 @@ import { Apr1 } from "./Apr1";
import { isFactionWork } from "../Work/FactionWork";
import { V2Modal } from "../utils/V2Modal";
import { MathJaxContext } from "better-react-mathjax";
import { useRerender } from "./React/hooks";
const htmlLocation = location;
@ -127,7 +128,7 @@ export function GameRoot(): React.ReactElement {
const classes = useStyles();
const [{ files, vim }, setEditorOptions] = useState({ files: {}, vim: false });
const [page, setPage] = useState(determineStartPage());
const setRerender = useState(0)[1];
const rerender = useRerender();
const [augPage, setAugPage] = useState<boolean>(false);
const [faction, setFaction] = useState<Faction>(
isFactionWork(Player.currentWork) ? Factions[Player.currentWork.factionName] : (undefined as unknown as Faction),
@ -155,9 +156,6 @@ export function GameRoot(): React.ReactElement {
setErrorBoundaryKey(errorBoundaryKey + 1);
}
function rerender(): void {
setRerender((old) => old + 1);
}
useEffect(() => {
return ITutorialEvents.subscribe(rerender);
}, []);

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React, { useEffect } from "react";
import Paper from "@mui/material/Paper";
import Typography from "@mui/material/Typography";
@ -27,6 +27,7 @@ import {
iTutorialSteps,
iTutorialEnd,
} from "../../InteractiveTutorial";
import { useRerender } from "../React/hooks";
interface IContent {
content: React.ReactElement;
@ -47,6 +48,7 @@ const useStyles = makeStyles((theme: Theme) =>
export function InteractiveTutorialRoot(): React.ReactElement {
const classes = useStyles();
const rerender = useRerender();
const tutorialScriptName = `n00dles.js`;
@ -556,11 +558,6 @@ export function InteractiveTutorialRoot(): React.ReactElement {
},
};
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
return ITutorialEvents.subscribe(rerender);
}, []);

@ -19,6 +19,7 @@ import { debounce } from "lodash";
import { Settings } from "../../Settings/Settings";
import { ANSIITypography } from "./ANSIITypography";
import { ScriptArg } from "../../Netscript/ScriptArg";
import { useRerender } from "./hooks";
let layerCounter = 0;
@ -53,10 +54,7 @@ interface Log {
let logs: Log[] = [];
export function LogBoxManager(): React.ReactElement {
const setRerender = useState(true)[1];
function rerender(): void {
setRerender((o) => !o);
}
const rerender = useRerender();
useEffect(
() =>
LogBoxEvents.subscribe((script: RunningScript) => {
@ -140,12 +138,9 @@ function LogWindow(props: IProps): React.ReactElement {
const classes = useStyles();
const container = useRef<HTMLDivElement>(null);
const textArea = useRef<HTMLDivElement>(null);
const setRerender = useState(false)[1];
const rerender = useRerender(1000);
const [size, setSize] = useState<[number, number]>([500, 500]);
const [minimized, setMinimized] = useState(false);
function rerender(): void {
setRerender((old) => !old);
}
const textAreaKeyDown = (e: React.KeyboardEvent) => {
if (e.ctrlKey && e.key === "a") {
@ -214,8 +209,6 @@ function LogWindow(props: IProps): React.ReactElement {
useEffect(() => {
updateLayer();
const id = setInterval(rerender, 1000);
return () => clearInterval(id);
}, []);
function kill(): void {

15
src/ui/React/hooks.ts Normal file

@ -0,0 +1,15 @@
import { useEffect, useState } from "react";
/** Hook that returns a function for the component. Optionally set an interval to rerender the component.
* @param autoRerenderTime: Optional. If provided and nonzero, used as the ms interval to automatically call the rerender function.
*/
export function useRerender(autoRerenderTime?: number) {
const setRerender = useState(false)[1];
const rerender = () => setRerender((old) => !old);
useEffect(() => {
if (!autoRerenderTime) return;
const intervalID = setInterval(rerender, autoRerenderTime);
return () => clearInterval(intervalID);
}, []);
return rerender;
}

@ -2,7 +2,7 @@ import { Box, Container, Paper, Table, TableBody, Tooltip } from "@mui/material"
import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography";
import { uniqueId } from "lodash";
import React, { useEffect, useState } from "react";
import React from "react";
import { Companies } from "../Company/Companies";
import { CONSTANTS } from "../Constants";
import { LocationName } from "../Enums";
@ -27,6 +27,7 @@ import { isGraftingWork } from "../Work/GraftingWork";
import { isFactionWork } from "../Work/FactionWork";
import { FactionWorkType } from "../Enums";
import { isCompanyWork } from "../Work/CompanyWork";
import { useRerender } from "./React/hooks";
const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle;
@ -193,15 +194,7 @@ function CrimeExpRows(rate: WorkStats): React.ReactElement[] {
}
export function WorkInProgressRoot(): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, CONSTANTS.MilliPerCycle);
return () => clearInterval(id);
}, []);
useRerender(CONSTANTS.MilliPerCycle);
let workInfo: IWorkInfo = {
buttons: {