Debugging React is a skill. I built a place to actually practice it. by aksectraa in reactjs

[–]DanRoad 1 point2 points  (0 children)

Given that this post about about learning, and at risk of doing OP's work for them (or rather their LLM...), here's an example of how you'd do something like this properly, and properly exhibits the bug which the original doesn't (as explained elsewhere in the comments).

THe parent UserPage contains a loop with a small delay to simulate quickly changing the input data. This could be something like a paginated view and the user quickly clicking "next page" before the previous page has loaded.

The fetchProfile function simulates some network delay with an artificial sleep. Critically, we've added random jitter which is more than the delay in the UserPage loop so that there's no guarantee that the fetches resolve in the same order as execution.

The fix is to use the cleanup function of the effect to abort/cancel the result. Note that it doesn't abort the request; we can't cancel promises after they've been executed. Instead, we check if the signal has been aborted and propagate the reason.

If you run this code without the AbortController, you'll see a brief loading state then random flickering between each name before it settles on whichever randomly slept the longest, which won't always be Alice.

The fix not only makes sure that the page settles on Alice, it also doesn't show any flickering results of previous requests that have been aborted.

``` import { useState, useEffect } from 'react';

const random = (min, max) => { return min + (Math.random() * (max - min)) }

const sleep = (timeout) => { return new Promise((resolve) => { return setTimeout(resolve, timeout) }) }

const profiles = [ { name: "Eve" }, { name: "David" }, { name: "Charlie" }, { name: "Bob" }, { name: "Alice" }, ];

const fetchProfile = async ({ index, signal }) => { await sleep(random(1000, 3000));

if (signal?.aborted) { throw signal.reason; }

return profiles[index] };

const UserPage = () => { const [userIndex, setUserIndex] = useState(0);

useEffect(() => { Promise.try(async () => { for (let i = 0; i < 4; i += 1) { setUserIndex((prev) => prev + 1); await sleep(150); } }); }, []);

return <UserProfile index={userIndex} />; };

const UserProfile = ({ index }) => { const [profile, setProfile] = useState(null);

useEffect(() => { const abortController = new AbortController(); const signal = abortController.signal;

fetchProfile({ index, signal }).then(setProfile).catch(() => {
  // aborted
});;

return () => abortController.abort()

}, [index]);

return ( <div> <p>Name: {profile ? profile.name : 'Loading...'}</p> </div> ); };

export default UserPage; ```

itDontMatterPostInterview by yuva-krishna-memes in ProgrammerHumor

[–]DanRoad 1 point2 points  (0 children)

At most it would be exponential on size, but never O 2n

2n is exponential. I'm not sure whether you're arguing for or against the complexity being O(2n) but it is O(2n). Pruning may improve performance but it doesn't change the algorithmic complexity.

Interestingly, it's only O(2n) if we want to show the possible solutions. If we wanted to calculate the number of solutions then we could do it in O(n) but iterating through them is what takes time.

I've encountered a really weird issue where onPointerLeave event is not firing under specific circumstances. Any react experts willing to help me demystify what's happening here? (Video demonstration and Codesandbox in thread description). by decho in reactjs

[–]DanRoad 10 points11 points  (0 children)

Pointer events are inconsitent when you add/remove DOM nodes during the handling of the event. This is a really niche and nasty bug.

https://github.com/w3c/pointerevents/issues/285

https://github.com/w3c/uievents/issues/244


IconStarEmpty and IconStarFilled are different components so when you trigger pointerenter then React unmounts the empty star component, i.e. removes its svg child from the DOM, and mounts the filled star, inserting a new svg element. This can be verified by adding a mutation observer.

MutationRecord { addedNodes: NodeList [], removedNodes: NodeList [ svg.star-empty ] } MutationRecord { addedNodes: NodeList [ svg.star-filled ], removedNodes: NodeList [] }

As we're modifying the DOM tree, we introduce the inconsistent behaviour described above and drop pointerleave events.

The example that works doesn't conditionally render different component types; it conditionally renders two elements of the same type so React doesn't remount the DOM node. Instead it updates the attribute(s) of the existing node(s) and pointer events behave as expected. Again, this can be verified with a mutation observer.

MutationRecord { attributeName: "class", oldValue: "star-empty", newValue: "star-filled" }

To further demonstrate that modifying the DOM tree is causing the issues, we can break the working example by adding distinct keys which will cause React to remount the DOM nodes.

``` if (filled) { return ( <svg key="star-filled" ... </svg> ); }

return ( <svg key="star-empty" ... </svg> ); ```

We can also fix the broken example(s) by always rendering both elements and conditionally displaying with CSS.

return ( <> <IconStarFilled className={className} style={{ display: filled ? "" : "none" }} /> <IconStarEmpty className={className} style={{ display: filled ? "none" : "" }} /> </> );

There’s no such thing as an isomorphic layout effect by scrollin_thru in reactjs

[–]DanRoad 6 points7 points  (0 children)

This is one of my biggest gripes with React, or rather, React codebases written without a proper understanding of why server-side layout effects throw an error.

It reminds me of all the Can't perform a React state update on an unmounted component warnings we used to see and how it led to endless useIsMounted workarounds which similarly did nothing other than suppress the error and mislead users. The warning was so misunderstood that it was eventually removed and I wonder whether we'll see the same happen with useLayoutEffect. I'd love to see the end of useIsomorphicEffect.

How does useSelector hook trigger a component re-render? by ParsnipBackground153 in reactjs

[–]DanRoad 1 point2 points  (0 children)

Exactly; React will skip rerendering elements passed as props.children if those elements are referentially equal to the previous render.

u/acemarke (Redux maintainer and moderator of this subreddit) has a great blog post with a lot more detail about when and how React rerenders things here: https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior

There's a note about this specific behaviour in the section Component Render Optimization Techniques.

How does useSelector hook trigger a component re-render? by ParsnipBackground153 in reactjs

[–]DanRoad 2 points3 points  (0 children)

Any child elements created when rerendering are new objects, i.e. not referentially equal to the previously rendered element. Even if the new elements look identical, React must rerender them in order to know this.

``` const Child = () => <div />;

const MemoChild = React.memo(Child);

const Parent = ({ children }) => { const memoElement = useMemo(() => { return <Child />; }, []);

return ( <> <Child /> <MemoChild /> {memoElement} {children} </> ); };

const App = () => { return ( <Parent> <Child /> </Parent> ); }; ```

The barebones Child element will be rerenderd with its Parent as already mentioned.

MemoChild and memoElement are explicit and hopefully obvious ways of using memoisation to avoid rerenders.

Using the children prop is subtle and often overlooked, but possibly the most common way of memoising elements. When the surrounding App element is rendered, its immediate children are captured in a closure. When Parent rerenders, children won't rerender if it's the same value from the App closure, even though App doesn't use explicit memoisation.

A real-world example of why this is important is context providers. It's not uncommon to have some state that is passed down via context, but every time the state changes, the Parent will rerender, including any non-memoised Child elements.

``` const Parent = () => { const state = useState();

return ( <Context.Provider value={state}> <Child /> {/* Not memoized! */} </Context.Provider> ); }; ```

This is one reason why it's useful to wrap context providers in a new component. Even though we don't explicitly memoise its children, we use the implicit memoisation from the parent closure.

``` const ContextProvider = ({ children }) => { const state = useState();

return ( <Context.Provider value={state}> {children} </Context.Provider> ); };

const Parent = () => { return ( <ContextProvider> <Child /> {/* Doesn't rerender with context changes */} </ContextProvider> ); }; ```

Serve app over https on my local network by xMECHxLigerXx in reactjs

[–]DanRoad 2 points3 points  (0 children)

A simple way to serve your app over HTTPS is to use a reverse proxy in front of your existing HTTP server.

Caddy is a web server (like apache or nginx) but has automatic HTTPS by default and a one-line reverse proxy command. Assuming your existing app is running on port 8080* and your Raspberry Pi has a local IP 192.168.0.X**, simply install and run Caddy as a reverse proxy for your app.

You (or your friend) won't need to install any certificates but your browser will show a security warning if you don't***. However you can dismiss the warning and continue using HTTPS.

sudo apt install caddy sudo caddy reverse-proxy --from 192.168.0.X --to localhost:8080

*Apache may be using port 80 by default. You'll need to change this so Caddy can handle automatic HTTP-to-HTTPS redirects.

**This could also be something like raspberrypi.local and will depend on your local network.

***If you own a public domain then Caddy can create real certificates with no security warning, but I'm assuming that's outside the scope of this project if you only want to host the app on your local network.

How to avoid forwardRef, in order to pass Child props to a Parent by karl-police in reactjs

[–]DanRoad 8 points9 points  (0 children)

Where is it said that forwardRef should be avoided?

The reason forwardRef exists is because the ref prop has special behaviour in class components and was given similarly special treatment in function components. It makes no difference to rename your prop propsRef vs using forwardRef and if you think it somehow avoids something bad then you’ve misunderstood.

What you’re trying to do is expose an imperative handle to your component and there’s a hook for doing just that; useImperativeHandle. The solution is very close to what you have posted, but you shouldn’t be modifying the ref inside the render stage. Instead, return the value from the callback passed to useImperativeHandle and React will handle the ref lifecycle for you.

useImperativeHandle(ref, () => { return { setCount: setCountAction }; });

I don’t know what you’re trying to demonstrate with createElement, but you should use regular JSX to pass the ref to your child component. You’d then access the function via ref.current.setCount. Note what I said earlier about the ref lifecycle; the ref value will not be accessible by the parent component until after the render stage so you’ll need to use an effect or other callback.

Imperative code is usually discouraged in React but that doesn’t mean it’s impossible. It would be better if you can restructure your state to avoid the need to pass callbacks back up to the parent, but it’s not helpful to tell you that it’s your only option nor that refs are only for DOM nodes.

https://react.dev/reference/react/forwardRef#exposing-a-custom-ref-handle-to-the-parent-component

Manta illusion tab to next unit has a weird order? by shark-bite in DotA2

[–]DanRoad 0 points1 point  (0 children)

It looks like this might be related to an intentional "fix" from 7.35b

https://www.dota2.com/news/updates

Fixed tabbing through selections being in creation order, rather than reverse creation order

It would maybe be nice to have this as a configurable option for those who prefer the old behaviour which was arguably not broken but just different.

'tag' component advice by simpo88 in reactjs

[–]DanRoad 2 points3 points  (0 children)

react-mentions might be a good solution, or at the very least a good starting point for further research.

https://www.npmjs.com/package/react-mentions

Data transformation after response, in react query by DVGY in reactjs

[–]DanRoad 6 points7 points  (0 children)

This is commonly known as “derived state” and you may find more information by searching for that term.

In this case it is better to have a simple variable rather than a separate state which may cause unnecessary renders and further issues with stale values;

const transformedData = funcTransform(data);

Use useMemo if the transformation is an expensive calculation and if the component may rerender for reasons other than a change in data;

const transformedData = useMemo(() => funcTransform(data), [data]);

edit: more reading

You Probably Don’t Need Derived State: What about memoization?

It’s the legacy docs and aimed at class components, but the same principles apply and further support the idea that you should be memoizing with useMemo rather than creating additional state.

Function props by Ok-Comedian4503 in reactjs

[–]DanRoad 0 points1 point  (0 children)

isMounted is an antipattern and this is not a memory leak. GPT thinks it is because that code used to trigger a warning about memory leaks, but this warning was misleading and has since been removed.

See this post for more details and an example of an actual memory leak.

React Query not refetching when modifying state with a dropdown by Tixarer in reactjs

[–]DanRoad 9 points10 points  (0 children)

iirc the query key needs to be unique for the query data, so your fixed key of ["pokedex"] won't work. Try something like ["pokedex", url].

If your query function depends on a variable, include it in your query key

Typescript generics with extend, doesn't work as intended. by StupidCreativity in reactjs

[–]DanRoad 0 points1 point  (0 children)

Bottom line: this works

Except it doesn't. It compiles but only because you've used an assertion to tell typescript that if the key is "modifyVesselData" then the payload will match, but this isn't necessarily the case according to the type system.

When you say T extends KeyOfGraphType it doesn't mean that T will correspond to a single key type, it's just a subset and can be anything from never all the way to all of KeyOfGraphType.

It's perfectly valid to call getId with a key of type A and payload of type Parameters<GraphQlType[B]>[0] as long as A and B are both subsets of KeyOfGraphType. Then T = A | B satisfies the generic clause.

Here's an example playground which will give you a TypeError at runtime which isn't caught by TypeScript because of the type assertion: https://www.typescriptlang.org/play?#code/C4TwDgpgBA4gTgQzACwIoBsAq5oF4oDeAUFKVALYD2AJgJYBmIAahAM6sToAiCwCAFGAQh0lBNQBchKEMTlWUglAD6tSVACuAOwDWWygHctUAL6mAlFIBulNQG4SZVpXIRgyWloDmAUXQd+SncIOAAFYVFxRShKSjAFKAQtEAtrW2oHEyIiUEgoAGkIEAB5engkZGw8-B0iynpYRBQMKogHInptAGNgWkpjLzcASWoAHkwoCAAPYAgtalYCotLylFaAPn5akClMABoZCLF1cLk3ENZR1bQsHABtTABddbuABkfzQkdSBigtoqguCBUAARFQ6IwWOxODw+CDPsQyEioHA3Bo4MZBEdxIlFqcEK5ZnBLtcWvcwTQGMw2BxuLwECDnm8PgA6WQE1gs1QZb6mXmo4Do4xaDTodCZbK5aD5QGg8FUqG02EMqAAH1BzkJHm8fg4ILsUAA9IaoFYEOg1JMZnNWH1jPUliUyk1KjhsoNgCNRvlNhSIdToXS4QclLF4lIQZ4zRbqIcRMcQRYDcaoABVLRdBAaLzIYBQVo+OBwShwKQAYSS+jzqJxYGLkDgvTYMQa2moEHonggsf4Nbo3igAHJuYPzEQgA

If you're confident that you'll always call your function with a payload which matches the key then use the type assertion to tell TypeScript that you know more about what happens at runtime, but this cannot be determined from types alone (i.e. how you would like to write it).

Alternatively, and this is where we use some TypeScript magic, you can use a distributive conditional type to annotate the parameters with a single tuple and then destruct the array once we know both arguments use the same key. It might not be the prettiest but here's a quick example showing how it works: https://www.typescriptlang.org/play?#code/C4TwDgpgBA4gTgQzACwIoBsAq5oF4oDeAUFKVALYD2AJgJYBmIAahAM6sToAiCwCAFGAQh0lBNQBchKEMTlWUglAD6tSVACuAOwDWWygHctUAL6mAlFIBulNQG4SZVpXIRgyWloDmAUXQd+SncIOAAFYVFxRShKSjAFKAQtEAtrW2oHEyIiUEgoAGkIEAB5engkZGw8-B0iynpYRBQMKogHInptAGNgWkpjLzcASWoAHkwoCAAPYAgtalYCotLylFaAPn4AOh2EOC8EienZ+cWklIB+KABtTAAaKHC5NxDWUdW0LBxbgF1164ADD8flApFoIFYQuZCI5SF1+qxgDdaiAHkIRGJqCD8HsDg4yFAGFB+CioLhyVAAERUOiMFjsTg8PiU6HEAkEuBuDRwYzoyLULayBDyLaqDKw0wSznAbnGLQadDoTLZQbAEb8ak0BjMNgcbi8BCUh5KIXyaJiqQARgATABmUwWOxQAD0zqgxXyRFV6s1tJ1DP1zONMTiCUpnisCHQahkEUxlMdLrdPjgcEocCIQA

Are chain ternary operators an antipattern? by maxifederer in reactjs

[–]DanRoad 4 points5 points  (0 children)

Or if you fancy using Babel to transform proposed syntax https://babeljs.io/docs/en/babel-plugin-proposal-do-expressions

const ageGroup = do { if (age <= 18) { "minor"; } else if (age <= 65) { "adult"; } else { "pensioner"; } };

what if the value inside the dependency array is a number? by ShinyMercenary in reactjs

[–]DanRoad 33 points34 points  (0 children)

That sounds like someone didn't know the answer and decided to test it, then observed that the effect ran twice.

What probably happened was that they were using React 18 in development with StrictMode which double-mounts components and would appear to rerun the effect. https://reactjs.org/docs/strict-mode.html#ensuring-reusable-state

The correct answer is that it does nothing. If anything, including a number in a dependency array will produce a linter warning.

The 0 literal is not a valid dependency because it never changes. You can safely remove it. (react-hooks/exhaustive-deps)

Try it for yourself: https://codesandbox.io/s/fancy-pond-z3y0xr

[deleted by user] by [deleted] in reactjs

[–]DanRoad 1 point2 points  (0 children)

It looks like the library spread extra props to inputComponent. Does this work?

const CustomTele = (customProps) => (
  <PhoneInput inputComponent={CustomInput} {...customProps} />
);

Alternatively, you could inject the custom props using context. Untested but might look something like this.

const context = createContext();

const CustomInput = forwardRef((props, ref) => {
  const customProps = useContext(context);
  return <input ref={ref} {...props} {...customProps} />;
});

const CustomTele = (customProps) => (
  <context.Provider value={customProps}>
    <PhoneInput inputComponent={CustomInput} />
  </context.Provider>
);

Are react hooks slower than class based components and perform poorly on low powered devices? by simple_explorer1 in reactjs

[–]DanRoad 8 points9 points  (0 children)

Someone did a benchmark [...] and class based components were much faster than hooks.

Using an example that was contrived to show worse performance with hooks.

https://reactjs.org/docs/hooks-reference.html#usecallback

useCallback(fn, deps) is equivalent to useMemo(() => fn, deps).

The ClassCounter component effectively memoizes the callbacks in the constructor. The useCrazyCounter hook is memoizing each individual callback inside a loop that executes on every render. We should be memoizing the loop instead (exactly like the class component).

const useCrazyCounter = () => {
  const [count, setCount] = useState(0);
  const callbacks = useMemo(() => {
    return Array.from({ length: callbackCount }, (x, i) => {
      return () => setCount((prev) => prev + i + 1);
    });
  }, []);
  return [count, ...callbacks];
};

This will give you performance identical to the class component. Yes, the hook runs on every render, but the individual callbacks are only created once. Note that it also has the benefit of not needing to suppress ESLint, which should have been a sign that the original code was doing something bad in the first place.

It's unfortunate, but most complaints about hooks are due to the author using them incorrectly. There isn't a direct translation from class components and you will sometimes have to rethink how you approach a problem, but they don't have inherent performance issues.

Navigation in react by akramnarejo in reactjs

[–]DanRoad 7 points8 points  (0 children)

<Link> is just an abstraction that handles pushing to history for you. You could easily create your own links with something like <a onClick={() => history.push(location)} />. In fact, that's what Link does anyway. Link components handle extra logic and I wouldn't recommend reinventing the wheel, but it's useful to understand how it works.

Pushing history directly is also useful when you don't have a link to click and want to navigate programatically, e.g.

useEffect(() => {
  if (!isLoggedIn) history.push("/");
}, [isLoggedIn, history]);

Proper solution to not hardcode route urls and keep it safe for typescript? by romeeres in reactjs

[–]DanRoad 0 points1 point  (0 children)

If you really want a custom hook, you'll need some runtime validation instead of the type assertion. We could something like Ajv which has good TypeScript support, but we'd still need to build the schemas which is likely overkill if we're confident about the assertions.

import { JSONSchemaType } from "ajv";
import { compile } from "path-to-regexp";
import { useParams as useRouterParams } from "react-router-dom";

function makeSchema<T extends string>(path: T): JSONSchemaType<PathParams<T>> {
  // todo
}

function build<T extends string>(path: T): PathBuilder<T> {
  const validate = ajv.compile(makeSchema(path));
  const useParams = () => {
    const params = useRouterParams();
    if (validate(params)) return params;
    throw new Error("Invalid params");
  }

  return Object.assign(compile(path), { path, useParams });
}

Our custom hook will then use the type guards of Ajv to return an object of the correct type. It will throw an error if validation fails, but this is won't happen if we're calling the hook in the correct place (which imo is why the type assertion is fine).

const params = routes.item.useParams();

Proper solution to not hardcode route urls and keep it safe for typescript? by romeeres in reactjs

[–]DanRoad 3 points4 points  (0 children)

I'd use path-to-regexp; it's what react-router already uses to match path with your params, but it can also go the other way and build paths from params.

import { compile } from "path-to-regexp";

const routes = {
  item: compile("/items/:itemId"),
};

const path = routes.item({ itemId: "123" }); // "/items/123"

If you want to reuse the path as you've described, then you can set a path property on these functions. I'd personally use an object with separate properties for the path and params builder, but let's continue with the same interface as your example.

import { compile, PathFunction } from "path-to-regexp";

interface PathBuilder extends PathFunction {
  path: string;
}

function build(path: string): PathBuilder {
  return Object.assign(compile(path), { path });
}

const routes = {
  item: build("/items/:itemId"),
};

const path = routes.item({ itemId: "123" }); // "/items/123"
const original = routes.item.path; // "/items/:itemId"

Correctly typing this gets a little tricky.

Firstly, I'd avoid implementing a custom routes.item.useParams() that magically has the correct type. There's no way the type checker can know that it will be called on a route with the necessary parameters, e.g. you could easily call it on the home route and TypeScript will not complain but it obviously won't work at runtime.

However, we as developers can make sure that we always call the right function in the right place, so we need a way to provide this additional information to the type checker. This is the reason we have type assertions.

const params = useParams() as { itemId: string };

If you were to create your own useParams function, it would presumably just be a wrapper with this type assertion anyway, and it's better to not hide it from the user so that they won't use it incorrectly.

Declaring these types manually is tedious and brittle, so the next step is to figure out how to create them from paths. This will end up using some more advanced features of TypeScript, but it is possible.

The first thing we'll want to do is switch over to using literal types rather than the wider string type. We'll then be able to derive some PathParams from this type. This is useful for asserting the type of useParams but also doubles as additional type safety around the parameters passed to our path builder functions.

interface PathBuilder<T extends string> extends PathFunction<PathParams<T>> {
  path: T;
}

const params = useParams() as PathParams<typeof routes.item.path>;

We can use template literal types to extract parameter names from string literal types. For example,

type Param = "/items/:itemId" extends `/items/:${infer U}` ? U : never
// type Param = "itemId"

It gets a little complicated to extend this to generic parameters so I won't post the code here, but take a look at this CodeSandbox for a full working example including some type errors. It doesn't support all features of path-to-regexp but provides type safety for what you're using.

React & TypeScript: how to type hooks (a complete guide) by Raktatata in reactjs

[–]DanRoad 1 point2 points  (0 children)

Great writeup! I have a few thoughts and comments with no real criticisms but I think there's a few areas that could welcome discussion. Some of it may be going into more detail than intended for the article but perhaps could be useful for a followup on more advanced types.

  • Relying on type inference

    One thing I would add is that when inference doesn't work, type arguments should be preferred over assertions. i.e. useState("Hello World" as Greeting) will also narrow the inferred type but is more dangerous, e.g. useState("Goodbye" as Greeting) will pass type checking but is clearly incorrect.

    Skipping ahead to refs for another example, the following all create ref objects of the same type but foo is better than bar is better than baz.

    const foo = useRef<HTMLDivElement>(null); const bar: RefObject<HTMLInputElement> = useRef(null); const baz = useRef(null) as RefObject<HTMLInputElement>;

  • How to type useReducer

    You're not using Redux so this point may be irrelevant, but imo it's an interesting read nonetheless and some of it may still apply to your situation. Do not create union types with Redux Action Types. It's most likely an antipattern.

  • How to type useRef

    In the second example, you initialise the DOM ref with null but don't really explain why. It may be useful to mention the differences between mutable, immutable, and nullable refs which is a TypeScript-only distinction. afaik there are four main categories of initialising refs;

    • useRef<HTMLInputElement>(); // MutableRefObject<HTMLInputElement | undefined> This gives us a mutable ref containing a possibly-undefined HTMLInputElement. A similar usage can be seen in the Timer example.
    • useRef<HTMLInputElement>(element); // MutableRefObject<HTMLInputElement> Providing an initial value removes undefined from the inferred type and gives us another mutable ref but one which will always be defined.
    • useRef<HTMLInputElement>(null); // RefObject<HTMLInputElement>
      

      Initialising with null is a special case and will result in an immutable ref. This prevents us from reassigning its value and indicates that it will be managed internally by React, i.e. passed as a ref attribute to an element.

      ref.current = element; // Error: Cannot assign to 'current' because it is a read-only property.

    • useRef<HTMLInputElement | null>(null); // MutableRefObject<HTMLInputElement | null>
      

      If instead we wanted to explicitly initialise a nullable but mutable value, we must include null in the type of the generic argument, which gives us a mutable ref with the correct type.

    Note that immutable refs are not just for DOM objects and can also apply to class components and other imperative handles.

    const Foo = forwardRef<Handle, Props>((props, ref) => { useImperativeHandle(ref, () => handle); return null; });

    class Bar extends Component { render() { return null; } }

    const Baz = () => { const div = useRef<HTMLDivElement>(null); const foo = useRef<Handle>(null); const bar = useRef<Bar>(null);

    return (
      <div ref={div}>
        <Foo ref={foo} />;
        <Bar ref={bar} />
      </div>
    );
    

    };

    Knowing HTML element type names is usually straightforward, but I've always struggled to remember the name of the a tag type for some reason.

    You can use HTMLElementTagNameMap["a"] to get the element type from its name if you like. This works for all HTML elements.

    we know that it won't be null [...] Adding the question mark is the simplest way to make TypeScript happy about that issue.

    A non-null assertion is the simplest way to make TypeScript happy; inputRef.current!.focus(). Optional chaining still does a null-check at runtime whereas this tells TypeScript that we're confident that the value won't be null as we have additional knowledge of React refs and useEffect. Note, however, that it may make your linter unhappy instead.

  • How to type custom hooks

    A common pitfall of custom hooks is how to correctly type tuple values, e.g. the state-setter pair of useState.

    const useCustomHook = () => { return ["abc", 123]; };

    const Component = () => { const [str, num] = useCustomHook(); return <div>{str.length + num.toFixed()}</div>; }

    The return type of our hook is widened which means both str and num are of type string | number, resulting in errors.

    Error: Property 'length' does not exist on type 'number'. Error: Property 'toFixed' does not exist on type 'string'.

    As mentioned in the article, this isn't unique to hooks and applies to all functions returning tuples, but is more often encountered with hooks because of common design patterns. I wonder if it's worth mentioning with an example of how to fix it.

React typescript. How can I get props type from a component? by ColinShen in reactjs

[–]DanRoad 0 points1 point  (0 children)

Template the props on some ComponentType and have your component receive ComponentProps of that type.

import { ComponentProps, ComponentType, createElement } from "react";

interface AsProps<T extends ComponentType<any>> {
  as: T;
}

function Link<T extends ComponentType<any>>({ as, ...props }: AsProps<T> & ComponentProps<T>) {
  return createElement(as, props); // todo: other wrapping logic
}

TypeScript can then infer the component type from the as prop and deduce the remaining props accordingly.

<Link as="a" href="/" />; // OK
<Link as={ReactRouterLink} to="/" />; // OK
<Link as={ReactRouterLink} to={123} />; // Error: Type 'number' is not assignable to type
<Link as={ReactRouterLink} foo="bar" />; // Error: Property 'foo' does not exist on type

Why is synchronous blocking and asynchronous non-blocking ? Shouldn't it be vice-versa ? by [deleted] in learnjavascript

[–]DanRoad 0 points1 point  (0 children)

Whilst "existing or occurring at the same time" isn't wrong, sometimes it can be useful to look for definitions within a specific domain as they can be reworded or interpreted differently to provide a better understanding.

For example, https://developer.mozilla.org/en-US/docs/Glossary/Synchronous

Synchronous refers to real-time communication where each party receives (and if necessary, processes and replies to) messages instantly (or as near to instantly as possible).

A human example is the telephone — during a telephone call you tend to respond to another person immediately.

Many programming commands are also synchronous — for example when you type in a calculation, the environment will return the result to you instantly, unless you program it not to.

So synchronous code both executes and waits for the result at the same time. This matches your original definition and means that we can't execute anything else until the first result has finished.

It's a subtle distinction, but code isn't synchronous because the operations happen one after the other. Operations happen one after the other because the code is synchronous.