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

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

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

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

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

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

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

@ -1,7 +1,7 @@
// React Components for the Corporation UI's navigation tabs // 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 // 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 // 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 { MainPanel } from "./MainPanel";
import { IndustryType } from "../data/Enums"; import { IndustryType } from "../data/Enums";
import { ExpandIndustryTab } from "./ExpandIndustryTab"; import { ExpandIndustryTab } from "./ExpandIndustryTab";
@ -11,22 +11,16 @@ import { Overview } from "./Overview";
import Tabs from "@mui/material/Tabs"; import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab"; import Tab from "@mui/material/Tab";
import { useRerender } from "../../ui/React/hooks";
export function CorporationRoot(): React.ReactElement { export function CorporationRoot(): React.ReactElement {
const rerender = useRerender(200);
const corporation = Player.corporation; const corporation = Player.corporation;
if (corporation === null) return <></>; if (corporation === null) return <></>;
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
const [divisionName, setDivisionName] = useState<string | number>("Overview"); const [divisionName, setDivisionName] = useState<string | number>("Overview");
function handleChange(event: React.SyntheticEvent, tab: string | number): void { function handleChange(event: React.SyntheticEvent, tab: string | number): void {
setDivisionName(tab); setDivisionName(tab);
} }
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
const canExpand = const canExpand =
Object.values(IndustryType).filter( Object.values(IndustryType).filter(

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

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

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

@ -10,6 +10,7 @@ import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch"; import Switch from "@mui/material/Switch";
import { CorpMaterialName } from "@nsdefs"; import { CorpMaterialName } from "@nsdefs";
import { materialNames } from "../../data/Constants"; import { materialNames } from "../../data/Constants";
import { useRerender } from "../../../ui/React/hooks";
interface ILeftoverProps { interface ILeftoverProps {
matName: CorpMaterialName; matName: CorpMaterialName;
@ -49,10 +50,7 @@ interface IProps {
export function SmartSupplyModal(props: IProps): React.ReactElement { export function SmartSupplyModal(props: IProps): React.ReactElement {
const division = useDivision(); const division = useDivision();
const setRerender = useState(false)[1]; const rerender = useRerender();
function rerender(): void {
setRerender((old) => !old);
}
// Smart Supply Checkbox // Smart Supply Checkbox
function smartSupplyOnChange(e: React.ChangeEvent<HTMLInputElement>): void { 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 { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { StaneksGiftEvents } from "../StaneksGiftEvents"; import { StaneksGiftEvents } from "../StaneksGiftEvents";
@ -11,16 +11,14 @@ import { ActiveFragment } from "../ActiveFragment";
import { Fragments } from "../Fragment"; import { Fragments } from "../Fragment";
import { DummyGrid } from "./DummyGrid"; import { DummyGrid } from "./DummyGrid";
import Container from "@mui/material/Container"; import Container from "@mui/material/Container";
import { useRerender } from "../../ui/React/hooks";
type IProps = { type IProps = {
staneksGift: StaneksGift; staneksGift: StaneksGift;
}; };
export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement { export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement {
const setRerender = useState(true)[1]; const rerender = useRerender();
function rerender(): void {
setRerender((o) => !o);
}
useEffect(() => StaneksGiftEvents.subscribe(rerender), []); useEffect(() => StaneksGiftEvents.subscribe(rerender), []);
return ( return (
<Container maxWidth="lg" disableGutters sx={{ mx: 0 }}> <Container maxWidth="lg" disableGutters sx={{ mx: 0 }}>

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

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

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

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

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

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

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

@ -3,7 +3,7 @@
* *
* This subcomponent renders all of the buttons for applying to jobs at a company * 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 Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";
@ -25,6 +25,7 @@ import { Page } from "../../ui/Router";
import { Player } from "@player"; import { Player } from "@player";
import { QuitJobModal } from "../../Company/ui/QuitJobModal"; import { QuitJobModal } from "../../Company/ui/QuitJobModal";
import { CompanyWork } from "../../Work/CompanyWork"; import { CompanyWork } from "../../Work/CompanyWork";
import { useRerender } from "../../ui/React/hooks";
type IProps = { type IProps = {
locName: LocationName; locName: LocationName;
@ -32,15 +33,8 @@ type IProps = {
export function CompanyLocation(props: IProps): React.ReactElement { export function CompanyLocation(props: IProps): React.ReactElement {
const [quitOpen, setQuitOpen] = useState(false); const [quitOpen, setQuitOpen] = useState(false);
const setRerender = useState(false)[1]; const rerender = useRerender(200);
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
/** /**
* We'll keep a reference to the Company that this component is being rendered for, * 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 * 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 { Money } from "../../ui/React/Money";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { useEffect, useState } from "react"; import { useRerender } from "../../ui/React/hooks";
export function HospitalLocation(): React.ReactElement { export function HospitalLocation(): React.ReactElement {
/** Stores button styling that sets them all to block display */ /** Stores button styling that sets them all to block display */
const btnStyle = { display: "block" }; const btnStyle = { display: "block" };
const setRerender = useState(false)[1]; const rerender = useRerender(200);
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
function getHealed(e: React.MouseEvent<HTMLElement>): void { function getHealed(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {

@ -14,7 +14,6 @@ interface IProps {
onClose: () => void; onClose: () => void;
ram: number; ram: number;
cost: number; cost: number;
rerender: () => void;
} }
/** React Component for the popup used to purchase a new server. */ /** 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 * 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 Button from "@mui/material/Button";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";
@ -15,15 +15,11 @@ import { Page } from "../../ui/Router";
import { Player } from "@player"; import { Player } from "@player";
import { Box } from "@mui/material"; import { Box } from "@mui/material";
import { Crime } from "../../Crime/Crime"; import { Crime } from "../../Crime/Crime";
import { useRerender } from "../../ui/React/hooks";
export function SlumsLocation(): React.ReactElement { export function SlumsLocation(): React.ReactElement {
const setRerender = useState(false)[1]; useRerender(1000);
const rerender = () => setRerender((o) => !o);
const crimes = Object.values(Crimes); const crimes = Object.values(Crimes);
useEffect(() => {
const timerId = setInterval(() => rerender(), 1000);
return () => clearInterval(timerId);
});
function doCrime(e: React.MouseEvent<HTMLElement>, crime: Crime) { function doCrime(e: React.MouseEvent<HTMLElement>, crime: Crime) {
if (!e.isTrusted) return; if (!e.isTrusted) return;

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

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

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

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

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

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react"; import React, { useEffect } from "react";
import { find } from "lodash"; import { find } from "lodash";
import { Box, Typography, Button, Container, Paper } from "@mui/material"; import { Box, Typography, Button, Container, Paper } from "@mui/material";
@ -11,14 +11,12 @@ import { Settings } from "../../Settings/Settings";
import { Programs } from "../Programs"; import { Programs } from "../Programs";
import { CreateProgramWork } from "../../Work/CreateProgramWork"; import { CreateProgramWork } from "../../Work/CreateProgramWork";
import { useRerender } from "../../ui/React/hooks";
export const ProgramsSeen: string[] = []; export const ProgramsSeen: string[] = [];
export function ProgramsRoot(): React.ReactElement { export function ProgramsRoot(): React.ReactElement {
const setRerender = useState(false)[1]; useRerender(200);
function rerender(): void {
setRerender((old) => !old);
}
const programs = [...Object.values(Programs)] const programs = [...Object.values(Programs)]
.filter((prog) => { .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 => { const getHackingLevelRemaining = (lvl: number): number => {
return Math.ceil(Math.max(lvl - (Player.skills.hacking + Player.skills.intelligence / 2), 0)); 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 libSource from "!!raw-loader!../NetscriptDefinitions.d.ts";
import { TextField, Tooltip } from "@mui/material"; import { TextField, Tooltip } from "@mui/material";
import { useRerender } from "../../ui/React/hooks";
interface IProps { interface IProps {
// Map of filename -> code // Map of filename -> code
@ -106,10 +107,7 @@ let currentScript: OpenScript | null = null;
// Called every time script editor is opened // Called every time script editor is opened
export function Root(props: IProps): React.ReactElement { export function Root(props: IProps): React.ReactElement {
const setRerender = useState(false)[1]; const rerender = useRerender();
function rerender(): void {
setRerender((o) => !o);
}
const editorRef = useRef<IStandaloneCodeEditor | null>(null); const editorRef = useRef<IStandaloneCodeEditor | null>(null);
const monacoRef = useRef<Monaco | null>(null); const monacoRef = useRef<Monaco | null>(null);
const vimStatusRef = useRef<HTMLElement>(null); const vimStatusRef = useRef<HTMLElement>(null);

@ -4,6 +4,7 @@ import IconButton from "@mui/material/IconButton";
import _ from "lodash"; import _ from "lodash";
import { Color, ColorPicker } from "material-ui-color"; import { Color, ColorPicker } from "material-ui-color";
import React, { useState } from "react"; import React, { useState } from "react";
import { useRerender } from "../../ui/React/hooks";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../ui/React/Modal";
import { OptionSwitch } from "../../ui/React/OptionSwitch"; 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 { export function ThemeEditorModal(props: IProps): React.ReactElement {
const setRerender = useState(false)[1]; const rerender = useRerender();
function rerender(): void {
setRerender((o) => !o);
}
// Need to deep copy the object since it has nested attributes // Need to deep copy the object since it has nested attributes
const [themeCopy, setThemeCopy] = useState<IScriptEditorTheme>(JSON.parse(JSON.stringify(Settings.EditorTheme))); 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 { InvitationsSeen } from "../../Faction/ui/FactionsRoot";
import { hash } from "../../hash/hash"; import { hash } from "../../hash/hash";
import { Locations } from "../../Locations/Locations"; import { Locations } from "../../Locations/Locations";
import { useRerender } from "../../ui/React/hooks";
const RotatedDoubleArrowIcon = React.forwardRef((props: { color: "primary" | "secondary" | "error" }, __ref) => ( const RotatedDoubleArrowIcon = React.forwardRef((props: { color: "primary" | "secondary" | "error" }, __ref) => (
<DoubleArrowIcon color={props.color} style={{ transform: "rotate(-90deg)" }} /> <DoubleArrowIcon color={props.color} style={{ transform: "rotate(-90deg)" }} />
@ -108,15 +109,7 @@ interface IProps {
} }
export function SidebarRoot(props: IProps): React.ReactElement { export function SidebarRoot(props: IProps): React.ReactElement {
const setRerender = useState(false)[1]; useRerender(200);
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
let flash: Page | null = null; let flash: Page | null = null;
switch (ITutorial.currStep) { switch (ITutorial.currStep) {

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

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

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

@ -2,30 +2,23 @@
* Root React Component for the "Active Scripts" UI page. This page displays * 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 * 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 Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab"; import Tab from "@mui/material/Tab";
import { ActiveScriptsPage } from "./ActiveScriptsPage"; import { ActiveScriptsPage } from "./ActiveScriptsPage";
import { RecentScriptsPage } from "./RecentScriptsPage"; import { RecentScriptsPage } from "./RecentScriptsPage";
import { WorkerScript } from "../../Netscript/WorkerScript"; import { WorkerScript } from "../../Netscript/WorkerScript";
import { useRerender } from "../React/hooks";
interface IProps { interface IProps {
workerScripts: Map<number, WorkerScript>; workerScripts: Map<number, WorkerScript>;
} }
export function ActiveScriptsRoot(props: IProps): React.ReactElement { 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"); const [tab, setTab] = useState<"active" | "recent">("active");
useRerender(200);
function handleChange(event: React.SyntheticEvent, tab: "active" | "recent"): void { function handleChange(event: React.SyntheticEvent, tab: "active" | "recent"): void {
setTab(tab); setTab(tab);
} }

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

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

@ -77,6 +77,7 @@ import { Apr1 } from "./Apr1";
import { isFactionWork } from "../Work/FactionWork"; import { isFactionWork } from "../Work/FactionWork";
import { V2Modal } from "../utils/V2Modal"; import { V2Modal } from "../utils/V2Modal";
import { MathJaxContext } from "better-react-mathjax"; import { MathJaxContext } from "better-react-mathjax";
import { useRerender } from "./React/hooks";
const htmlLocation = location; const htmlLocation = location;
@ -127,7 +128,7 @@ export function GameRoot(): React.ReactElement {
const classes = useStyles(); const classes = useStyles();
const [{ files, vim }, setEditorOptions] = useState({ files: {}, vim: false }); const [{ files, vim }, setEditorOptions] = useState({ files: {}, vim: false });
const [page, setPage] = useState(determineStartPage()); const [page, setPage] = useState(determineStartPage());
const setRerender = useState(0)[1]; const rerender = useRerender();
const [augPage, setAugPage] = useState<boolean>(false); const [augPage, setAugPage] = useState<boolean>(false);
const [faction, setFaction] = useState<Faction>( const [faction, setFaction] = useState<Faction>(
isFactionWork(Player.currentWork) ? Factions[Player.currentWork.factionName] : (undefined as unknown as 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); setErrorBoundaryKey(errorBoundaryKey + 1);
} }
function rerender(): void {
setRerender((old) => old + 1);
}
useEffect(() => { useEffect(() => {
return ITutorialEvents.subscribe(rerender); 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 Paper from "@mui/material/Paper";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
@ -27,6 +27,7 @@ import {
iTutorialSteps, iTutorialSteps,
iTutorialEnd, iTutorialEnd,
} from "../../InteractiveTutorial"; } from "../../InteractiveTutorial";
import { useRerender } from "../React/hooks";
interface IContent { interface IContent {
content: React.ReactElement; content: React.ReactElement;
@ -47,6 +48,7 @@ const useStyles = makeStyles((theme: Theme) =>
export function InteractiveTutorialRoot(): React.ReactElement { export function InteractiveTutorialRoot(): React.ReactElement {
const classes = useStyles(); const classes = useStyles();
const rerender = useRerender();
const tutorialScriptName = `n00dles.js`; 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(() => { useEffect(() => {
return ITutorialEvents.subscribe(rerender); return ITutorialEvents.subscribe(rerender);
}, []); }, []);

@ -19,6 +19,7 @@ import { debounce } from "lodash";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { ANSIITypography } from "./ANSIITypography"; import { ANSIITypography } from "./ANSIITypography";
import { ScriptArg } from "../../Netscript/ScriptArg"; import { ScriptArg } from "../../Netscript/ScriptArg";
import { useRerender } from "./hooks";
let layerCounter = 0; let layerCounter = 0;
@ -53,10 +54,7 @@ interface Log {
let logs: Log[] = []; let logs: Log[] = [];
export function LogBoxManager(): React.ReactElement { export function LogBoxManager(): React.ReactElement {
const setRerender = useState(true)[1]; const rerender = useRerender();
function rerender(): void {
setRerender((o) => !o);
}
useEffect( useEffect(
() => () =>
LogBoxEvents.subscribe((script: RunningScript) => { LogBoxEvents.subscribe((script: RunningScript) => {
@ -140,12 +138,9 @@ function LogWindow(props: IProps): React.ReactElement {
const classes = useStyles(); const classes = useStyles();
const container = useRef<HTMLDivElement>(null); const container = useRef<HTMLDivElement>(null);
const textArea = 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 [size, setSize] = useState<[number, number]>([500, 500]);
const [minimized, setMinimized] = useState(false); const [minimized, setMinimized] = useState(false);
function rerender(): void {
setRerender((old) => !old);
}
const textAreaKeyDown = (e: React.KeyboardEvent) => { const textAreaKeyDown = (e: React.KeyboardEvent) => {
if (e.ctrlKey && e.key === "a") { if (e.ctrlKey && e.key === "a") {
@ -214,8 +209,6 @@ function LogWindow(props: IProps): React.ReactElement {
useEffect(() => { useEffect(() => {
updateLayer(); updateLayer();
const id = setInterval(rerender, 1000);
return () => clearInterval(id);
}, []); }, []);
function kill(): void { 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 Button from "@mui/material/Button";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { uniqueId } from "lodash"; import { uniqueId } from "lodash";
import React, { useEffect, useState } from "react"; import React from "react";
import { Companies } from "../Company/Companies"; import { Companies } from "../Company/Companies";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { LocationName } from "../Enums"; import { LocationName } from "../Enums";
@ -27,6 +27,7 @@ import { isGraftingWork } from "../Work/GraftingWork";
import { isFactionWork } from "../Work/FactionWork"; import { isFactionWork } from "../Work/FactionWork";
import { FactionWorkType } from "../Enums"; import { FactionWorkType } from "../Enums";
import { isCompanyWork } from "../Work/CompanyWork"; import { isCompanyWork } from "../Work/CompanyWork";
import { useRerender } from "./React/hooks";
const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle; const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle;
@ -193,15 +194,7 @@ function CrimeExpRows(rate: WorkStats): React.ReactElement[] {
} }
export function WorkInProgressRoot(): React.ReactElement { export function WorkInProgressRoot(): React.ReactElement {
const setRerender = useState(false)[1]; useRerender(CONSTANTS.MilliPerCycle);
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, CONSTANTS.MilliPerCycle);
return () => clearInterval(id);
}, []);
let workInfo: IWorkInfo = { let workInfo: IWorkInfo = {
buttons: { buttons: {