The author supplies repository-specific variants for this Skill. Each section below applies to the named repository. Author documentation. Base metadata.
Revision: 03e6fbb8b3bf507a2ec8aefc827a71ee8507a6ea
Variant for metamask-extension
Source: domains/performance/skills/perf-hooks-effects/repos/metamask-extension.md
Front-End Performance Rules: Hooks & Effects
This file covers React hooks and effects optimization rules including useEffect best practices, dependency management, cleanup patterns, and preventing cascading re-renders.
Rule: Don't Overuse useEffect
DO:
- Calculate derived state during render instead of using useEffect
- Only use useEffect for side effects (data fetching, DOM manipulation, subscriptions)
DON'T:
- Use useEffect for derived state that can be calculated during render
Reference: See: You Might Not Need an Effect
Example - WRONG:
const TokenDisplay = ({ token }: TokenDisplayProps) => {
const [displayName, setDisplayName] = useState('');
useEffect(() => {
setDisplayName(`${token.symbol} (${token.name})`);
}, [token]);
return <div>{displayName}</div>;
};
Example - CORRECT:
const TokenDisplay = ({ token }: TokenDisplayProps) => {
const displayName = `${token.symbol} (${token.name})`;
return <div>{displayName}</div>;
};
Rule: Minimize useEffect Dependencies
DO:
- Depend on the primitive the effect reads (an ID, an address, a number) rather than an object rebuilt on each render
- Keep values the effect does not use out of the effect
DON'T:
- Include unnecessary dependencies that cause effects to run too often
- Drop a value the effect reads to make it run less often. A default parameter does not stop it being a dependency (see Rule: Include All Dependencies in useEffect)
Example - WRONG:
const TokenBalance = ({ account, network, refreshInterval }: Props) => {
const [balance, setBalance] = useState('0');
useEffect(() => {
const fetch = async () => {
const result = await fetchBalance(account.address, network);
setBalance(result);
};
fetch();
const interval = setInterval(fetch, refreshInterval);
return () => clearInterval(interval);
}, [account, network, refreshInterval]); // A parent passing account={{ address }} rebuilds it on every render, so the effect refetches and resets the interval each time
};
Example - CORRECT:
const TokenBalance = ({ account, network, refreshInterval }: Props) => {
const [balance, setBalance] = useState('0');
const { address } = account;
useEffect(() => {
const fetch = async () => {
const result = await fetchBalance(address, network);
setBalance(result);
};
fetch();
const interval = setInterval(fetch, refreshInterval);
return () => clearInterval(interval);
}, [address, network, refreshInterval]); // The address string is equal across renders
};
Rule: Never Use JSON.stringify in useEffect Dependencies
DO:
- Use useEqualityCheck hook for deep equality checks (recommended)
- Use useRef with deep equality check in effect
- Normalize to stable primitives when possible
- Use createDeepEqualSelector for Redux selectors
DON'T:
- Use JSON.stringify in dependencies (expensive, unreliable, breaks with functions/circular refs)
- Use useMemo with JSON.stringify (defeats purpose)
Why: JSON.stringify executes on every render, string comparison is slower than reference comparison, creates new string objects defeating memoization, can cause infinite loops, and doesn't handle circular references or functions.
When You Need Deep Equality:
- Nested properties of an object change (deep equality)
- Array elements change (deep equality)
- Object reference changes but values are the same (should NOT trigger)
Example - WRONG:
const usePolling = (input: PollingInput) => {
useEffect(() => {
startPolling(input);
}, [input && JSON.stringify(input)]); // Expensive! Runs every render
};
Example - CORRECT: Option 1 - Use useEqualityCheck hook (Recommended)
import { useEqualityCheck } from './hooks/useEqualityCheck';
import { isEqual } from 'lodash';
const usePolling = (input: PollingInput) => {
const stableInput = useEqualityCheck(input, isEqual);
useEffect(() => {
startPolling(stableInput);
}, [stableInput]); // Only triggers when deep values actually change
};
Example - CORRECT: Option 2 - useRef with deep equality check
import { isEqual } from 'lodash';
const usePolling = (input: PollingInput) => {
const inputRef = useRef(input);
useEffect(() => {
// Only execute if deep values changed
if (!isEqual(input, inputRef.current)) {
inputRef.current = input;
startPolling(input);
}
}, [input]);
};
Example - CORRECT: Option 3 - Normalize to stable primitives
const usePolling = (input: PollingInput) => {
const inputId = useMemo(() => input.id, [input.id]);
const inputChainId = useMemo(() => input.chainId, [input.chainId]);
const inputRpcUrl = useMemo(() => input.rpcUrl, [input.rpcUrl]);
useEffect(() => {
startPolling(input);
}, [inputId, inputChainId, inputRpcUrl]); // Only depends on primitives
};
When to Use Each Approach:
| Approach | Use When | Pros | Cons |
|---|---|---|---|
| useEqualityCheck | Objects/arrays from props or external state | Simple, reusable, handles edge cases | Requires hook import |
| useRef + isEqual | One-off cases, custom logic needed | Full control, no extra hook | More boilerplate |
| Normalize to primitives | Can extract stable IDs/values | Most performant, clear dependencies | Not always possible |
Key Principles:
- Use deep equality when object references change frequently but values don't
- Prefer useEqualityCheck hook - Already implemented in codebase
- Normalize when possible - Extract stable primitives (IDs, strings, numbers)
- Never use JSON.stringify
- Don't skip dependencies - Always include dependencies, use deep equality to stabilize them
Rule: Include All Dependencies in useEffect
DO:
- Include all values used in the effect in the dependency array
- Use useRef with a flag if you truly only want to track once
DON'T:
- Use empty dependency arrays when values from closure are used (creates stale closures)
- Skip dependencies to avoid re-running effects
Example - WRONG:
const Name = ({ type, name }: NameProps) => {
useEffect(() => {
trackEvent({
properties: {
petname_category: type, // Uses 'type' from closure
has_petname: Boolean(name?.length), // Uses 'name' from closure
},
});
}, []); // Empty deps - 'type' and 'name' are stale!
};
Example - CORRECT:
const Name = ({ type, name }: NameProps) => {
useEffect(() => {
trackEvent({
properties: {
petname_category: type,
has_petname: Boolean(name?.length),
},
});
}, [type, name]); // Include all dependencies
// OR if you truly only want to track once:
const hasTrackedRef = useRef(false);
useEffect(() => {
if (!hasTrackedRef.current) {
trackEvent({
properties: {
petname_category: type,
has_petname: Boolean(name?.length),
},
});
hasTrackedRef.current = true;
}
}, [type, name]);
};
Rule: Include All Dependencies in useMemo/useCallback
DO:
- Always include all dependencies in useMemo/useCallback dependency arrays
- Use ESLint rule react-hooks/exhaustive-deps to catch missing dependencies
DON'T:
- Skip dependencies (causes stale closures and bugs)
Example - WRONG:
const TokenList = ({ tokens, filter }: TokenListProps) => {
const filteredTokens = useMemo(() => {
return tokens.filter((token) => token.symbol.includes(filter));
}, [tokens]); // Missing filter dependency!
};
Example - CORRECT:
const TokenList = ({ tokens, filter }: TokenListProps) => {
const filteredTokens = useMemo(() => {
return tokens.filter((token) => token.symbol.includes(filter));
}, [tokens, filter]); // All dependencies included
};
Rule: Avoid Cascading useEffect Chains
DO:
- Combine effects or compute during render using useMemo
- Use single effect for async operations
DON'T:
- Create multiple effects where one sets state that triggers another (causes unnecessary re-renders)
Example - WRONG:
const useHistoricalPrices = () => {
const [prices, setPrices] = useState([]);
const [metadata, setMetadata] = useState(null);
// First effect fetches and updates Redux
useEffect(() => {
fetchPrices();
const intervalId = setInterval(fetchPrices, 60000);
return () => clearInterval(intervalId);
}, [chainId, address]);
// Second effect responds to Redux state change
useEffect(() => {
const pricesToSet = historicalPricesNonEvm?.[address]?.intervals ?? [];
setPrices(pricesToSet); // Triggers third effect
}, [historicalPricesNonEvm, address]);
// Third effect depends on state from second effect
useEffect(() => {
const metadataToSet = deriveMetadata(prices);
setMetadata(metadataToSet);
}, [prices]);
};
Example - CORRECT:
const useHistoricalPrices = () => {
// Compute prices during render from Redux state
const prices = useMemo(() => {
return historicalPricesNonEvm?.[address]?.intervals ?? [];
}, [historicalPricesNonEvm, address]);
// Compute metadata during render from prices
const metadata = useMemo(() => {
return deriveMetadata(prices);
}, [prices]);
// Single effect for async operations
useEffect(() => {
fetchPrices();
const intervalId = setInterval(fetchPrices, 60000);
return () => clearInterval(intervalId);
}, [chainId, address]);
return { prices, metadata };
};
Rule: Avoid Conditional Early Returns with All Dependencies
DO:
- Split effects when conditional logic excludes some dependencies
- Ensure the effect reads every dependency (see Rule: Don't List Dependencies the Effect Doesn't Read)
DON'T:
- Include dependencies that aren't used when condition is true
Example - WRONG:
const useHistoricalPrices = ({ isEvm, chainId, address }: Props) => {
useEffect(() => {
if (isEvm) {
return; // Early return
}
// Only uses chainId and address when not EVM
fetchPrices(chainId, address);
}, [isEvm, chainId, address]); // Includes all deps even when unused
};
Example - CORRECT:
// Option 1: Split effects
const useHistoricalPrices = ({ isEvm, chainId, address }: Props) => {
useEffect(() => {
if (!isEvm) {
fetchPrices(chainId, address);
}
}, [isEvm, chainId, address]); // All deps are used
};
Rule: Don't List Dependencies the Effect Doesn't Read
The dependency array describes the effect's code. A value that must restart the effect when it changes has to be a value the effect uses.
DO:
- Treat a dependency the effect never reads as a finding, even when the effect is meant to restart when it changes
- If the effect's work depends on the value, pass the value into that work: the call that starts the connection, subscription or background registration
- If the value marks a scope whose state must all reset when it changes, render that scope as a component and give it the value as its
key - If the value changes because of a specific action, do the work where that action is handled instead of in an effect that watches for the change
DON'T:
- Delete the dependency without one of the changes above. The effect then keeps a session bound to the old value
- Keep the unread dependency and explain it in a comment. React Compiler's effect-dependency validation (
validateExhaustiveEffectDependencies, lint rulereact-hooks/exhaustive-effect-dependencies, off by default) reports it as unnecessary, and with that validation on, the compiler leaves the whole component or hook uncompiled, or fails the build under a strictpanicThreshold - Add
'use no memo'to protect the dependency array. It opts the whole function out of React Compiler, and React documents it as a temporary debugging tool - Add a parameter the called code ignores just so the effect reads the value. That moves the unread value into the call
Reference: Removing Effect Dependencies, Resetting all state when a prop changes, "use no memo"
Example - WRONG:
const useSession = ({ address, networkId }: Props) => {
useEffect(() => {
const session = startSession(address);
return () => session.stop();
}, [address, networkId]); // networkId is never read
};
Example - CORRECT:
// The session is for one network, so the effect passes the network in
const useSession = ({ address, networkId }: Props) => {
useEffect(() => {
const session = startSession({ address, networkId });
return () => session.stop();
}, [address, networkId]);
};
// Caches must be cleared when the account or network changes:
// a keyed child unmounts on the change, and its cleanup clears them
const CacheScope = () => {
useEffect(() => () => clearCaches(), []);
return null;
};
const Root = ({ address, networkId }: Props) => (
<CacheScope key={`${address}:${networkId}`} />
);
Rule: Use useRef for Persistent Values
DO:
- Use useRef for values that need to persist across renders
- Use refs for mounted flags, intervals, and other persistent state
DON'T:
- Use regular variables for values that need to persist (they get reset on every render)
Example - WRONG:
const usePolling = (input: PollingInput) => {
let isMounted = false; // Gets reset every render!
useEffect(() => {
isMounted = true;
startPolling(input);
return () => {
isMounted = false;
};
}, [input]);
};
Example - CORRECT:
const usePolling = (input: PollingInput) => {
const isMountedRef = useRef(false);
useEffect(() => {
isMountedRef.current = true;
startPolling(input);
return () => {
isMountedRef.current = false;
};
}, [input]);
};
Rule: Always Call Hooks Unconditionally
DO:
- Always call hooks in the same order on every render
- Use conditional logic inside hooks, not conditional hook calls
DON'T:
- Call hooks conditionally (breaks Rules of Hooks)
- Create hooks dynamically or in loops
Example - WRONG:
const TokenDisplay = ({ token, showDetails }: TokenDisplayProps) => {
const [balance, setBalance] = useState('0');
if (showDetails) {
// ⚠️ Hook called conditionally - breaks Rules of Hooks!
const [metadata, setMetadata] = useState(null);
useEffect(() => {
fetchMetadata(token.id).then(setMetadata);
}, [token.id]);
}
return <div>{balance}</div>;
};
Example - CORRECT:
const TokenDisplay = ({ token, showDetails }: TokenDisplayProps) => {
const [balance, setBalance] = useState('0');
const [metadata, setMetadata] = useState(null);
useEffect(() => {
if (showDetails) {
fetchMetadata(token.id).then(setMetadata);
}
}, [token.id, showDetails]);
return (
<div>
<div>Balance: {balance}</div>
{showDetails && metadata && <div>Metadata: {metadata.name}</div>}
</div>
);
};
Example - WRONG: Dynamic hook creation
const AssetList = ({ assets }: AssetListProps) => {
// ⚠️ Number of hooks changes based on assets.length!
const balances = assets.map((asset) => {
const [balance, setBalance] = useState('0'); // Wrong!
useEffect(() => {
fetchBalance(asset.id).then(setBalance);
}, [asset.id]);
return balance;
});
};
Example - CORRECT:
// Option 1: Custom hook for single asset
const useAssetBalance = (assetId: string) => {
const [balance, setBalance] = useState('0');
useEffect(() => {
fetchBalance(assetId).then(setBalance);
}, [assetId]);
return balance;
};
const AssetList = ({ assets }: AssetListProps) => {
return (
<div>
{assets.map(asset => (
<AssetItem key={asset.id} asset={asset} />
))}
</div>
);
};
// Option 2: Component with its own hooks
const AssetItem = ({ asset }: { asset: Asset }) => {
const balance = useAssetBalance(asset.id);
return <div>{asset.name}: {balance}</div>;
};
Rule: Prevent Cascading Re-renders from Hook Dependencies
DO:
- Use selectors and memoization to break re-render chains
- Isolate hook dependencies by extracting stable values
- Use component composition to prevent unnecessary re-renders
DON'T:
- Create effects that depend on frequently changing values without memoization
Example - WRONG:
const Dashboard = () => {
const accounts = useSelector((state) => state.accounts); // Large array
const [filteredAccounts, setFilteredAccounts] = useState([]);
// Effect runs whenever accounts array reference changes
useEffect(() => {
const filtered = accounts.filter((a) => a.isActive);
setFilteredAccounts(filtered); // Triggers re-render
}, [accounts]); // accounts reference changes frequently
// Another effect depends on filteredAccounts
useEffect(() => {
updateAnalytics(filteredAccounts); // Triggers another update
}, [filteredAccounts]);
};
Example - CORRECT:
// In selectors file:
const selectAccounts = (state) => state.accounts;
const selectActiveAccounts = createSelector([selectAccounts], (accounts) =>
accounts.filter((a) => a.isActive),
);
// In component:
const Dashboard = () => {
// Selector handles memoization - only changes when accounts actually change
const activeAccounts = useSelector(selectActiveAccounts);
// Memoize analytics update to prevent unnecessary calls
const analyticsRef = useRef(activeAccounts);
useEffect(() => {
if (analyticsRef.current !== activeAccounts) {
updateAnalytics(activeAccounts);
analyticsRef.current = activeAccounts;
}
}, [activeAccounts]);
};
Example - WRONG: Hook depends on frequently changing object
const TokenCard = ({ token }: TokenCardProps) => {
const [formattedBalance, setFormattedBalance] = useState('');
useEffect(() => {
// token object reference changes frequently
setFormattedBalance(formatBalance(token.balance, token.decimals));
}, [token]); // Re-runs too often
};
Example - CORRECT:
const TokenCard = ({ token }: TokenCardProps) => {
// Extract primitive values that change less frequently
const balance = token.balance;
const decimals = token.decimals;
// Calculate during render instead of effect
const formattedBalance = useMemo(
() => formatBalance(balance, decimals),
[balance, decimals]
);
return <div>{formattedBalance}</div>;
};
Rule: Use Component Composition to Prevent Re-renders
DO:
- Move state down to components that need it
- Pass children as props to prevent re-renders
- Isolate state changes to specific components
DON'T:
- Keep all state at the top level causing unnecessary re-renders
Example - WRONG:
const Dashboard = () => {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
<ExpensiveChart /> {/* Re-renders unnecessarily! */}
<ExpensiveTable /> {/* Re-renders unnecessarily! */}
</div>
);
};
Example - CORRECT: Move state down
const Dashboard = () => {
return (
<div>
<Counter /> {/* Only this re-renders */}
<ExpensiveChart />
<ExpensiveTable />
</div>
);
};
const Counter = () => {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
};
Example - CORRECT: Pass children as props
const Dashboard = ({ children }: { children: React.ReactNode }) => {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
{children} {/* Children don't re-render! */}
</div>
);
};
// Usage:
<Dashboard>
<ExpensiveChart />
<ExpensiveTable />
</Dashboard>
Rule: Prevent State Updates After Component Unmount
DO:
- Check mounted state before updating state in async operations
- Use cancelled flag pattern with cleanup
DON'T:
- Update state after component unmount (causes memory leaks and React warnings)
Example - WRONG:
const TokenBalance = ({ address }: TokenBalanceProps) => {
const [balance, setBalance] = useState('0');
useEffect(() => {
fetchBalance(address).then((result) => {
setBalance(result); // ⚠️ May update after unmount!
});
}, [address]);
};
Example - CORRECT:
const TokenBalance = ({ address }: TokenBalanceProps) => {
const [balance, setBalance] = useState('0');
useEffect(() => {
let cancelled = false;
const fetch = async () => {
const result = await fetchBalance(address);
if (!cancelled) {
setBalance(result);
}
};
fetch();
return () => {
cancelled = true;
};
}, [address]);
};
Rule: Use AbortController for Fetch Requests
DO:
- Use AbortController to cancel fetch requests on unmount
- Check if request was aborted before updating state
- Handle AbortError appropriately
DON'T:
- Leave fetch requests running after component unmount
Example - WRONG:
const AssetList = ({ chainId }: AssetListProps) => {
const [assets, setAssets] = useState([]);
useEffect(() => {
fetch(`/api/assets/${chainId}`)
.then((res) => res.json())
.then((data) => setAssets(data)); // Request continues after unmount!
}, [chainId]);
};
Example - CORRECT:
const AssetList = ({ chainId }: AssetListProps) => {
const [assets, setAssets] = useState([]);
useEffect(() => {
const controller = new AbortController();
fetch(`/api/assets/${chainId}`, { signal: controller.signal })
.then((res) => res.json())
.then((data) => {
if (!controller.signal.aborted) {
setAssets(data);
}
})
.catch((error) => {
if (error.name !== 'AbortError') {
console.error('Failed to fetch assets:', error);
}
});
return () => {
controller.abort(); // Cancels request on unmount
};
}, [chainId]);
};
Rule: Clean Up Intervals and Subscriptions
DO:
- Always clean up intervals, timeouts, and subscriptions in useEffect cleanup
- Use cancelled flag pattern for async operations in intervals
DON'T:
- Leave intervals or subscriptions running after unmount
Example - WRONG:
const PriceTicker = ({ tokenAddress }: PriceTickerProps) => {
const [price, setPrice] = useState(0);
useEffect(() => {
const interval = setInterval(async () => {
const newPrice = await fetchPrice(tokenAddress);
setPrice(newPrice);
}, 1000); // ⚠️ Interval continues after unmount!
// Missing cleanup!
}, [tokenAddress]);
};
Example - CORRECT:
const PriceTicker = ({ tokenAddress }: PriceTickerProps) => {
const [price, setPrice] = useState(0);
useEffect(() => {
let cancelled = false;
const fetchPrice = async () => {
const newPrice = await fetchPriceData(tokenAddress);
if (!cancelled) {
setPrice(newPrice);
}
};
fetchPrice(); // Initial fetch
const interval = setInterval(fetchPrice, 1000);
return () => {
cancelled = true;
clearInterval(interval); // Cleanup on unmount
};
}, [tokenAddress]);
};
Rule: Keep Multi-Step Async Protocols Out of the Effect
DO:
- When an effect acquires an external resource through more than one async step, with a timeout or more than one way to end, move that protocol into a plain object owned outside React:
start()returns a handle, andhandle.stop()releases the resource - Keep the effect to acquiring and releasing: start in the body, stop in the cleanup
- Unit-test the object's end paths directly (ready, timeout, failure, stop while a step is in flight), without rendering
DON'T:
- Grow a state machine of closure flags (
cancelled,ended,ready) inside the effect. Every end path needs its own cleanup, every fix has to reason about every flag at once, and the only way to test it is throughrenderHook
Why: every line of such an effect can pass the rules above (stable dependencies, timers cleared, a cancelled flag on the async chain) while the bugs sit between its end paths: a timeout that fires mid-acquire, a release sent before registration, a release that also runs on the success path. Those bugs belong to the protocol, not to any one line, so a line-level review does not catch them.
Example - WRONG:
useEffect(() => {
let cancelled = false;
let ended = false;
let ready = false;
const finish = (reason: string) => {
if (!ended) {
ended = true;
report(reason);
}
};
const timeout = setTimeout(() => {
cancelled = true;
finish('timeout');
release(id);
}, 30_000);
register(id)
.then(() => !cancelled && init(address))
.then(() => !cancelled && start(id))
.then(() => {
if (!cancelled) {
ready = true;
finish('ready');
}
})
.catch(() => {
finish('failed');
if (!cancelled) release(id);
})
.finally(() => clearTimeout(timeout));
return () => {
cancelled = true;
clearTimeout(timeout);
finish('released');
release(id, ready);
};
}, [address]);
Example - CORRECT:
// preload-session.ts: a plain object, unit-tested without React
export function startPreloadSession(address: string): { stop: () => void } {
// register, init, start, the timeout, and every end path live here
}
// The hook only acquires and releases
useEffect(() => {
const session = startPreloadSession(address);
return () => session.stop();
}, [address]);
Rule: Avoid Large Object Retention in Closures
DO:
- Extract only needed data from large objects
- Use refs for stable references to avoid capturing large objects in closures
- Minimize what's captured in closure scope
DON'T:
- Capture large objects in closures (prevents garbage collection)
Example - WRONG:
const TransactionList = ({ transactions }: TransactionListProps) => {
const [filtered, setFiltered] = useState([]);
useEffect(() => {
// Large transactions array captured in closure
const expensiveFilter = () => {
return transactions
.filter((tx) => tx.status === 'pending')
.map((tx) => expensiveTransform(tx)); // Large object retained!
};
const interval = setInterval(() => {
setFiltered(expensiveFilter());
}, 5000);
return () => clearInterval(interval);
}, [transactions]); // transactions array reference changes frequently
};
Example - CORRECT:
const TransactionList = ({ transactions }: TransactionListProps) => {
const [filtered, setFiltered] = useState([]);
const transactionsRef = useRef(transactions);
// Update ref without causing effect to re-run
useEffect(() => {
transactionsRef.current = transactions;
}, [transactions]);
useEffect(() => {
let cancelled = false;
const expensiveFilter = () => {
// Use ref to avoid capturing transactions in closure
const currentTransactions = transactionsRef.current;
return currentTransactions
.filter((tx) => tx.status === 'pending')
.map((tx) => ({
id: tx.id,
amount: tx.amount,
// Only extract needed properties, not entire object
}));
};
const updateFiltered = () => {
if (!cancelled) {
setFiltered(expensiveFilter());
}
};
updateFiltered();
const interval = setInterval(updateFiltered, 5000);
return () => {
cancelled = true;
clearInterval(interval);
};
}, []); // Empty deps - uses ref instead
};
License
MIT License
Copyright (c) 2026 MetaMask
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.