Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

RFC: React Hooks#68

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
sebmarkbage merged 3 commits intomasterfromhooks-rfc
Nov 21, 2018
Merged

RFC: React Hooks#68

sebmarkbage merged 3 commits intomasterfromhooks-rfc
Nov 21, 2018

Conversation

sebmarkbage
Copy link
Collaborator

@sebmarkbagesebmarkbage commentedOct 25, 2018
edited by gaearon
Loading

In this RFC, we propose introducingHooks to React. See the RFC and the documentation for more details.

https://reactjs.org/docs/hooks-overview.html

View formatted RFC

(Thetalk video has now been published too.)


Nov 16 Update by@sophiebits:

See@sebmarkbage’s comment#68 (comment) for a detailed response to the preceding comments that covers various tradeoffs and articulates why we’ve landed on the current design. If you have something to say, please check that it is not answered there already.

j-f1, alexeyraspopov, slapxxi, markerikson, mrsvt, agarun, cangoektas, OneCyrus, jamesplease, cheapsteak, and 1703 more reacted with thumbs up emojitwoshot, ValeriaVG, aVolpe, rcreasi, salvoravida, ChrisWiles, omeid, MatthewHerbst, samiskin, SethDavenport, and 64 more reacted with thumbs down emojirgehan, kc-beard, selbekk, Taym95, geeofree, erathe, shripadk, kidandcat, kceb, andres9722, and 98 more reacted with laugh emojijergason, tmarnet, eps1lon, philipp-spiess, Ephem, alexeyraspopov, ypconstante, RodrigoCarvalhoCode, slapxxi, mrsvt, and 541 more reacted with hooray emojiyurivish, FlorianKoerner, MatthewHerbst, samiskin, omeid, Taym95, kana-sama, bitfactory-bodhy-bik, ThomasJuster, jbe456, and 52 more reacted with confused emojialexeyraspopov, slapxxi, mrsvt, agarun, cangoektas, jamesplease, voluntadpear, pheuter, michalakjan, NE-SmallTown, and 669 more reacted with heart emojiGuruM, danielkcz, emrekeskinmac, anyuxuan, sadiqmmm, thinklinux, carlosvq, TravnikovDev, codemilli, bgnx, and 31 more reacted with rocket emojiGuruM, danielkcz, StefanSokic, anyuxuan, TravnikovDev, codemilli, bgnx, rodamaral, Sachin124, anshumanv, and 10 more reacted with eyes emoji
In this RFC, we propose introducing *Hooks* to React. See the RFC and the documentation for more details.https://reactjs.org/docs/hooks-overview.html
@grabbou
Copy link

Question about persisting values ofuseState - is the preferred way to create a customusePersistedState hook that calls into storage mechanism?

I was wondering if there was a way to batch the write operations in environments such as React Native and how did you approach that problem at Facebook during your initial adoption?

eldh, AlexPoirier1, leoyli, MisterAJ, amritbluefin, redian, RPDeshaies, alfondotnet, ShokiiCookiie, shuzootani, and 15 more reacted with thumbs up emojibgnx, florida0723, and Yukiniro reacted with thumbs down emojibgnx reacted with laugh emojibgnx reacted with hooray emojibgnx and Cezary-Janicki reacted with confused emojibgnx and cmcodes-vw reacted with heart emojibgnx, p666888, jaguayomunoz, umutlaguler, woshiljf, and cmcodes-vw reacted with rocket emojibgnx, hoangph271, bole-wu, thaddydore, woshiljf, shourovfoisal, and qarun685 reacted with eyes emoji

@pcmaffey
Copy link

Is this the right place for feedback? First of all - amazing work. I love where this is going with composability. My big question is about performance of Hooks.

Are all these function hooks redefined and recalled on every render?

styfle, tanyapowell, benjamn, prsn, iagomls, nulladdict, lmatteis, alexandre, grxy, victorfern91, and 131 more reacted with thumbs up emojisnnsnn, hongbhuang, and bgnx reacted with thumbs down emojibgnx reacted with hooray emojibgnx, Createwj, karthikcodes6, and qarun685 reacted with heart emojiCreatewj and umutlaguler reacted with rocket emojiCreatewj and whoiswsw reacted with eyes emoji

@pheuter
Copy link

Just watched the React Conf keynote, awesome presentation, super excited to give hooks a shot!

Not sure if this is the right place to ask but was wondering how function components that make use of hooks affect server-side rendering. InuseEffects() for example, will errors be thrown when accessing browser apis likewindow during ssr?

GonchuB, tischsoic, tamv, azl397985856, colemannerd, warlock, boomyao, norarcasey, MustaMohamed, dominic-p, and 7 more reacted with thumbs up emoji

@pheuter
Copy link

@pcmaffey Looks likethis FAQ entry addresses your performance concern.

Stanko, pcmaffey, grabbou, dceddia, RoyalIcing, iagomls, klzns, danilyanich, AlexPoirier1, piperchester, and 17 more reacted with thumbs up emoji

@JoshuaKGoldberg
Copy link

JoshuaKGoldberg commentedOct 25, 2018
edited
Loading

Very exciting, this looks awesome!

Questioning the tuple return type:

const[count,setCount]=useState(0);

Why an array tuple instead of an object containingcount andsetCount members? Is this for performance(and if so, do we have numbers to show browsers or Node behaving strongly differently)? It feels like leaving the returned object open to adding more fields might make it easier to remain backwards compatible in the future, similar to how APIs often end up taking in single objects as a parameter rather than long lists of parameters.

const{ value, set}=useState(0);

Edit: yes, per@jaredLunde's comment, the properties would need constant names.

TimothyBJacobs, smussell, danilyanich, mariuslundgard, macku, alxtz, zunsthy, BrendanFDMoore, mbarish-me, humancatfood, and 23 more reacted with thumbs up emojiMadumo, subodhpareek18, jamiebuilds, aVolpe, btd, kylealwyn, haipengz, vladshcherbin, minheq, threehams, and 127 more reacted with thumbs down emojiShenxiaoy, loia5tqd001, and guoliben reacted with laugh emojistephenshank and workspaceking reacted with rocket emoji

@jaredLunde
Copy link

jaredLunde commentedOct 25, 2018
edited
Loading

@JoshuaKGoldberg How would you customize those object properties? Array is the cleanest way it works with custom naming.

ericelliott, chelmertz, kc-beard, kay-is, renato-bohler, ilias-t, crswll, MatthewHerbst, haipengz, mohsinulhaq, and 141 more reacted with thumbs up emojijt3k reacted with thumbs down emojianother-guy, juanmanuelramallo, heyi, ystarlongzi, gpincheiraa, raajnadar, aanavaneeth, wuyongyu, LuckyTomb, and MehdiRaash reacted with heart emojistephenshank and MisdirectionJ reacted with rocket emoji

@alexeyraspopov
Copy link

alexeyraspopov commentedOct 25, 2018
edited
Loading

Why an array tuple instead of an object containing count and setCount members?

You can assign names you need. Object destructuring requires you to put specific keys. You may end up having something like

let{state:name,setState:setName}=useState('Name');let{state:surname,setState:setSurname}=useState('Surname');

Which is not the case with tuples.

grabbou, trueadm, matchai, ermik, ozannnn, renatoagds, eps1lon, sjsn, vcanales, nwalters512, and 270 more reacted with thumbs up emojiermik, renatoagds, yeion7, hannadrehman, another-guy, covertbert, azl397985856, ystarlongzi, atayahmet, alexwendte, and 6 more reacted with hooray emojibrainkim, danthegoodman, threehams, ansgarm, amelekhin, ericf89, adrianmcli, litinskii, NE-SmallTown, limichange, and 39 more reacted with confused emojiermik, renatoagds, adielhercules, rcreasi, AlexPoirier1, tvsudhir3, another-guy, behnammodi, RiCoTeRoX, TanaseHagi, and 18 more reacted with heart emoji

@jamesplease
Copy link

jamesplease commentedOct 25, 2018
edited
Loading

I'm still familiarizing myself with this API, but I'm optimistic about these changes. Great work, React team!

My main concern with hooks is related to the learning curve. There are some APIs introduced that new (and seasoned) developers may not immediately pick up on.

One in particular that stands out the most to me isuseEffect(myEffect, []). It's not very expressive. For those who haven't read the docs, what do you think this does? I'd be surprised if someone could guess.

For those who are still familiarizing themselves with the API, it calls the effect only on mounting and unmounting, and not on updates (docs here).

I know that this proposal introduces a lot of new functions, but it might be worthwhile adding another one that functions the same asuseEffect(myEffect, []). As for the name? I'm not sure. Perhaps something likeuseMountEffect()?

quantizor, Madumo, atticoos, jelkand, brotzky, brunolemos, hardikmodha, rickyvetter, ehynds, taoxiang1995, and 103 more reacted with thumbs up emojisnnsnn, jay51, adipascu, and bohdanz1997 reacted with thumbs down emojianother-guy, OEvgeny, AlvYuste, elertan, hexrcs, vinay30, robertleib, sanatankc, httpJunkie, xvjg, and alexcamerondev reacted with heart emoji

@eps1lon
Copy link
Member

It is a very early time for Hooks, so some integrations like DevTools support or Flow/TypeScript typings may not be ready yet.

Will I be able to inspect what hooks a component is using?

Having an object with named properties as state made it easy to see what's what. I guess this will be hard with the newuseState?

benjamn, johnste, bxt, rmacqueen, adrianmcli, another-guy, brucou, interphx, gnapse, souporserious, and 20 more reacted with thumbs up emoji

@benjamn
Copy link

@eps1lon According to theFAQ,useState is backed by a list of anonymous memory cells:

There is an internal list of “memory cells” associated with each component. They’re just JavaScript objects where we can put some data. When you call a Hook likeuseState(), it reads the current cell (or initializes it during the first render), and then moves the pointer to the next one. This is how multipleuseState() calls each get independent local state.

So if you ever find yourself trying to debug the underlying state, you're probably going to be working with a linked list of unnamed values. To figure out which memory cell is which, you'll have to refer back to the order of youruseState calls.

Full disclosure: I'm planning to open an issue today to discuss a system for providing meaningful names for youruseState calls, which should significantly relax the current usage restrictions, and eliminate the need for a linter to enforce those rules.

peggyrayzis, quantizor, justingrant, slushyash, eps1lon, disquisition, skellyb, fongandrew, juliensnz, lvnr, and 80 more reacted with thumbs up emojiHypnosphi, kevinSuttle, and gandazgul reacted with hooray emojiHypnosphi, bvalyou, denis-sokolov, httpJunkie, VitGottwald, claudiopro, gandazgul, and MohamedLamineAllal reacted with heart emoji

@maddie927
Copy link

maddie927 commentedOct 25, 2018
edited
Loading

Positional effects (using any of theseuse* functions is an effect, even just to read state) worry me. It seems easy to get wrong, and doing so might not present until production. For the same reason it also seems like it'll be extremely difficult to introduce to beginners -- "the magic function works here but not there".

UsingSymbols instead of positional keys would make it easier to understand and harder to get wrong:

const$count=Symbol("count");functionMyComponent(){const[count,setCount]=useState($count,0);  ...}

Edit for clarification: I don't mean to say the Symbol would be a global key -- it would still use the current context (the Fiber, I assume) as the primary key and the Symbol as the secondary key (where the effectful call order is currently being used).

Another edit for followup: After playing with hooks a bit more I don't feel as strongly about this as I thought I would. It's not perfect, but you could also pass the wrong Symbol in this alternative as well. Also seeing the eslint plugin catching on relieves the worry I had a bit too.

quantizor, justingrant, jbaxleyiii, benjamn, jelkand, mmartinson, dantman, skellyb, welliam, zgotsch, and 98 more reacted with thumbs up emojisqts, danakt, vovacodes, reyrodrigues, MeiKatz, GifCo, ymzuiku, jcestibariz, jacobbogers, ZNackasha, and 3 more reacted with thumbs down emojifpuddu, KL13NT, AlexPoirier1, adrianmcli, NE-SmallTown, danny-andrews-snap, eliassotodo, andresattler, mraucorp, Drakota, and jacobbogers reacted with confused emojianother-guy, Hypnosphi, denis-sokolov, a-tarasyuk, andrecoelho, john-d-pelingo, FezVrasta, and MoonBall reacted with heart emoji

@adamhaile
Copy link

Just curious, are React hooks at all inspired byS andSurplus? There are similarities down to even the names and terminology used. S hasS.effect() andS.cleanup(), anduseState() is analogous to S'sS.data().

For instance, here's the hooks example in Surplus:

functionExample(){constcount=S.data(0);// S.data instead of React.useStateS.effect(()=>{// S.effect instead of React.useEffectdocument.title=`You clicked${count()} times`;});return(<div><p>You clicked{count()} times</p><buttononClick={()=>count(count()+1)}>        Click me</button></div>);}

In fact, I used almost this exact same example in the Surplus FAQ to explainhow functional components could have state -- the very same issue hooks are solving for React.

I'm the author of S and Surplus, and reading the hook docs gave me a strong case of déjà vu! The similarities may be coincidental -- we're probably reading the same upstream sources. But if there was any influence, I thought that would be cool :).

AriaMinaei, Neaox, tsteuwer-accesso, salttis, sean-vieira, haipengz, ricardocanelas, vladshcherbin, BNursultan, hen-x, and 78 more reacted with thumbs up emojinoahbenham, adrianmcli, embiem, mrjjwright, Obi-Dann, rrag, gnapse, OEvgeny, ajitid, streamich, and 25 more reacted with heart emoji

@alexeyraspopov
Copy link

alexeyraspopov commentedOct 25, 2018
edited
Loading

@alqamabinsadiq, sorry if my comment was somehow confusing. This is not how the hook intended to be used, I was making an example of additional effort required for using object destructuring. This is how the correct code looks like:

let[name,setName]=useState('Name');let[surname,setSurname]=useState('Surname');

Less effort to write, less effort to read.

marianboda, adrianmcli, mustah, br0p0p, infodusha, Cretezy, puskuruk, and MuYunyun reacted with thumbs up emoji

@sophiebits
Copy link
Member

@adamhaile To my knowledge we weren’t influenced at all by Surplus (this is my first time seeing it). It’s true the “effect” name is similar, but both names are descriptive, so I guess it shouldn’t be too surprising that the names overlap.

quantizor, adamhaile, fpuddu, rcreasi, c0b41, haipengz, ricardocanelas, danilyanich, btdiaz34, NE-SmallTown, and 26 more reacted with thumbs up emojiAStaroverov, mishelen, a-tarasyuk, vinay30, jacobbogers, and nolotz reacted with thumbs down emojijlmakes, hardikmodha, ricardocanelas, Youngestdev, streamich, adrianolsk, mraucorp, raajnadar, bluebill1049, jacobbogers, and 2 more reacted with laugh emojidanivivriti, AInoob, philihp, a-tarasyuk, vinay30, bluebill1049, jacobbogers, Pau1fitz, and MoonBall reacted with confused emojiliuxing95 reacted with eyes emoji

@sebmarkbage
Copy link
CollaboratorAuthor

@benjamn@spicydonuts If you decide to publish a follow up RFC with named hooks which doesn’t have the conditionality constraints, then be sure to cover other pitfalls that you may also get. In the current proposal there is clearly one thing that will unexpectedly break when you don’t have names. However there are other things that might become confusing too. Such as that an effect doesn’t cleanup and refire when it’s in a conditional. So even with names you might find it best practice to avoid conditionals.

pspeter3, hen-x, danilyanich, yangshun, yacinehmito, NE-SmallTown, breathe, kevinSuttle, rosko, edgesoft, and 9 more reacted with thumbs up emoji

@maddie927
Copy link

I see what you mean. Hmm.. that's unfortunate 🤔

rosko and dengkunli reacted with thumbs up emoji

@ghost
Copy link

I like the idea, but I think the naming doesn't really reflect what's going on here. The termhook in other contexts, such as web hooks or Emacs, implies that something happens at a particular point in time and calls out to some other functions/methods to make it happen.

In the words of theGithub docs for Web hooks:

Webhooks allow you to build or set up GitHub Apps which subscribe to certain events on GitHub.com. When one of those events is triggered, we'll send a HTTP POST payload to the webhook's configured URL.

For example web hooks are called whenever Github detects a branch has been created. They're called in Emacs whenever a file is loaded or a particular mode (for syntax highlighting + shortcuts) is activated.

In React, the only hooks I can think of at the moment are the componentDidMount and componentWillUnmount functions, since they're part of a lifecycle and call out to some other function (as part of being overridden in a Component class)

From the React Hooks documentation it looks like these aremanagers:

  • useState manages and coordinates state
  • useEffect manages and supervises side-effects
  • useContext manages context

Other options for naming:

  • manager
  • supervisor
  • coordinator
  • handler

I'm sure there are other, better names other than "hook" which can be used.

Also, the naming of the methods don't need to change since none of them seem to have the word "hook" in them, the naming change would just affect the rest of the documentation/examples.

andrewmcwatters, jordonbiondo, noinkling, suhaildawood, johnste, me717, danilyanich, kdojeteri, snnsnn, KL13NT, and 63 more reacted with thumbs up emojitimoxley, albinekb, fizwidget, iy3r, Gregoor, caisah, damaon, MaxDesiatov, adrianolsk, 0rvar, and 10 more reacted with thumbs down emojifoxlele2014 reacted with hooray emojiyuqingc, Aprillion, juanmanuelramallo, and zamarrowski reacted with confused emojijordonbiondo, another-guy, CapySloth, ignitenet-martynas, SomodiTeodor, le-du6, and KSrahul reacted with heart emoji

@leebyron
Copy link

Loving this new API, but I have one primary concern - where the hooks come from.

I'm concerned that these hook functions are provided by the top level react module and imply (or require) global state to work. Ideally, hooks and effects functions could be provided to the functional component when it's executed.

So while this example as illustrated by Dan requires from the module:

const { useContext } = require('react')const SomeContext = require('./SomeContext)function Example({ someProp }) {  const contextValue = useContext(SomeContext)  return <div>{someProp}{contextValue}</div>}

Was it considered to pass the hooks into the functional component?

Perhaps looking something like:

const SomeContext = require('./SomeContext)function Example({ someProp }, hooks) {  const contextValue = hooks.useContext(SomeContext)  return <div>{someProp}{contextValue}</div>}

This means functional components which use hooks are(Props, Hooks) => ReactNode. While this is still not "pure" because hooks have effects and are not pure, at least the renderer could provide the appropriate hooks per call-site instead of relying on global state. I could imagine that the Fiber renderer might have different implementation of hooks than some potential future renderer, a server-side renderer, a test renderer with mocked effects, etc.

Thoughts?

justingrant, benjamn, grabbou, yanglinz, marvinhagemeister, nulladdict, RobinMalfait, wongmjane, lbdremy, AlvaroBernalG, and 196 more reacted with thumbs up emojieldh, sveisvei, holywyvern, marlonmarcello, Madumo, dincozdemir, sophie-gg, gtoma4, and FezVrasta reacted with thumbs down emojiAlicanC, dashed, robertleib, and mrjackdavis reacted with hooray emojiadrianmcli and AvraamMavridis reacted with confused emojiWizyma, sean-vieira, AlicanC, bxt, rmacqueen, AlexPoirier1, another-guy, octaviogb, twop, sandrina-p, and 14 more reacted with heart emoji

@kartikag01
Copy link

for new react developers, useState hooks will be a magic.

brunolemos, pspeter3, mauriciomelo, tomdohnal, AugustinLF, aVolpe, nezdemkovski, DanCouper, eps1lon, iagomls, and 91 more reacted with thumbs up emojiMatthewHerbst, timoxley, marzelc, snnsnn, KL13NT, MaxDesiatov, kafein, JacobTheEvans, and fforres reacted with thumbs down emojichunrapeepat, OEvgeny, NE-SmallTown, andreiduca, khanhhua, BALEHOK, marlonmarcello, mingshengxiao, rodrigouz, and Drakota reacted with laugh emojiMatthewHerbst, nk1tz, illourr, JacobTheEvans, and Pau1fitz reacted with confused emojicolemannerd and GzhiYi reacted with heart emoji

@ntucker
Copy link

How does this interact with React.memo()? Does it just shallow check props and ignore state and context. If not, how does it know what state and context to check?

sean-vieira, johnste, sergiodxa, peternoordijk, khanhhua, Zenwolf, fdaciuk, tylerwray, mrjackdavis, rodrigocfd, and Luminqi reacted with thumbs up emoji

@alexeyraspopov
Copy link

I'm concerned that these hook functions are provided by the top level react module and imply (or require) global state to work. Ideally, hooks and effects functions could be provided to the functional component when it's executed.

@leebyron, I think, the way how it works, is by using an incapsulated access to the current rendering fiber that holds the state and effects. The API may look like something "global" happens under the hood, but it is still scoped to the specific fiber that reflects the component that is rendering at the moment.

kdojeteri, NE-SmallTown, MohamedLamineAllal, 0rvar, and circlle reacted with thumbs up emojibxt, octaviogb, lennerd, twop, and mrjackdavis reacted with confused emoji

@benjamn
Copy link

@leebyron Alternatively, functional components could be invoked by the React internals with a meaningfulthis object, containing any number of useful methods.

denis-sokolov, mraucorp, juandjara, and HelloXZX reacted with thumbs up emojitibdex, hph, mhink, kylealwyn, nezdemkovski, vladshcherbin, minheq, peternycander, traviscooper, osmarks, and 49 more reacted with thumbs down emojidanilyanich and andfaulkner reacted with confused emoji

@david
Copy link

What's the advantage of this approach over a HOC-based one such as the one used, for example, inrecompose?

mauriciomelo, SethDavenport, MatthewHerbst, minheq, fendy3002, kenkz447, bxt, marzelc, danilyanich, dpikt, and 18 more reacted with thumbs up emojialbinekb, dabit3, codahk, covertbert, hc-12, apenab, amir-s, heya-aron, arseniy-gl, and ocsfwarch reacted with thumbs down emoji

@sebmarkbage
Copy link
CollaboratorAuthor

@leebyron Conceptually they’re algebraic effects. It just happens that the engine (a pointer to the current “handler”) lives in the react package. Note that the actual implementation doesn’t live in the react-package. So anyone can implement their own dispatcher. Even inside a react render. If this is a popular pattern you could just have a general purpose pattern for this like “dispatcher” package or something.

Just like context there doesn’t have to be one. In multi-threaded environments there can be several.

We considered passing it but then you would have to pass it through multiple layers of indirection. We already know this to be a pain (which is why we have context to begin with) and for something as light weight as custom hooks it is even more so.

brunolemos, RoyalIcing, rickhanlonii, AriaMinaei, aweary, mhink, petetnt, sean-vieira, smaclell, yeion7, and 45 more reacted with thumbs up emojilennerd, chenwenqin, and KieSun reacted with thumbs down emojijmurzy, danilyanich, andfaulkner, steida, MohamedLamineAllal, xiel, and springuper reacted with heart emoji

@jaredLunde
Copy link

@davidacdlite/recompose@7867de6

david, ConAntonakos, brunolemos, lasseborly, rgehan, litemerafrukt, helfi92, handsssome, haipengz, grzegorznyga, and 34 more reacted with thumbs up emojizakjholt, balthazar, lizongze, and MatthewHerbst reacted with laugh emojimicha149, arthurgubaidullin, and revanthCopart reacted with confused emojiadrianmcli, MeiKatz, mrjackdavis, and lizongze reacted with heart emoji

@sebmarkbage
Copy link
CollaboratorAuthor

@benjamn Thethis was actually in the first idea and later dropped because it added weirdness and got even weirder when composed with custom hooks. Since they would need to change to a different variable or use .call.

alexeyraspopov, sebmarkbage, brunolemos, jetpacmonkey, juliensnz, pspeter3, rgehan, rickhanlonii, k15a, danreeves, and 28 more reacted with thumbs up emoji

@quantizor
Copy link

Are there any concerns about the interaction of transpilers with state hooks? The order of function calls could conceivably be rearranged by a transpiler or minifier in a way that differs between an SSR and client-side bundle.

zgotsch, AlicanC, osmarks, epmatsw, AndreiCalazans, rodrigocipriani, marcneander, NE-SmallTown, hansott, sarimarton, and 11 more reacted with thumbs up emoji

@gaearon
Copy link
Member

gaearon commentedMar 29, 2019
edited
Loading

@timkendrick

In your proposed design,props becomes an argument ofuseEffect callback. Perhaps I misunderstood how that works. But it seems like this breaks encapsulation quite a bit because any custom Hook now suddenly has access to its owner component's props. That makes them non-compositional. One Hook could read props (assuming specific ones exist), and then later you might change the component to not receive that prop. Or you might lift a Hook out of a component, and the Hook will break. There's no obvious way that you'd know to update the custom Hook to not depend on it. (I talk a bit about spooky action at distance like thishere.)

It's also not entirely clear to me how you'd do something like this:

functionuseCustomThing(value){useEffect(()=>{doSomethingWith(value)})}functionMyComponent({ prop}){let[state,setState]=useState('')// ...letderived=useMemo(()=>expensiveStuff(prop,state),[prop,state])useCustomThing(derived)// ...}

In other words, it's not just "props and state" that need to be captured in effects and other closures, but anything that's calculated from them.Custom Hooks need access to the current values as their arguments — not just initial values. For example I don't have a clear idea of how you'd write thisuseFriendStatus Hook wherefriendID is anargument, and the Hook doesn't make assumptions that it comes fromprops.id (because custom Hooks aren't supposed to be aware of component's props).

Additionally, Hooks should be able to chain. So you should be able to do

constx=useStuff(props.foo)consty=useStuff(x)constz=useStuff(y)

IfuseStuff internally needs its argument inside an effect, it can't just doprops => props.foo anyway. (Even if that was compositional — which it is not.)

Your proposal about accessing at "wrong time" also doesn't solve the core of the issue. It's not that there is a "right" and "wrong" time to access it. It's that we want closures tocapture the values we rendered with, and to keep "seeing" those values forever. That's really important for concurrent mode where a notion ofcurrent value doesn't really exist. Hooks design models a component as being in many non-clashing states at the same time, instead of switching the "current" state (which is what classes model well). People don't really need to think about these details, but they're motivating the design a lot.


One thing that slightly irks me is your assumption that we didn't want to really take this feedback into account, or that we just decided to ignore the discussion. Or that we didn't consider this design.

I understand it might seem this way because my comment responding to your specific proposal wasn't very long. But as we mentioned earlier,realistically responding to every single proposal in detail would be a full-time job. I tried my best inthis blog post but there's only so much you can document, and people will still come up with slightly different combinations. I'm sorry if our responses haven't been sufficient to satisfy your curiosity. Up until the release day, we read all the proposals, even if we didn't respond to all of them.

I hope my questions above shine some light on why this particular variation didn't make it through. I will try my best to respond to follow-ups on this thread but I hope you can also understand that five months later after publishing the proposal, and given we also thought about it for several months before implementing (including this variation), responding to new slight variations that suffer from the same issues as older variations is not a top priority. (Thanks a lot for the bug report though!)

j-f1, puskuruk, ignas-sedunovas, AlexGalays, and quxbaz reacted with thumbs up emojiMathspy, curran, danielkcz, timkendrick, and j-f1 reacted with heart emoji

@timkendrick
Copy link

timkendrick commentedMar 29, 2019
edited
Loading

Thanks@gaearon – you're totally right, I was relying all the(props) => … functions because the statically-defined hooks needsome way of accessing the current props, but I completely overlooked the fact that this breaks encapsulation in custom hooks. I take your point on the 'chainability' as well. As far as I'm aware, these hadn't come up in previous criticisms of a factory approach so I thought I was coming up with a novel solution, but it appears you've already addressed this – I can only apologise for coming on so strong about that suggestion, I was only considering the 'capturing' criticism and didn't spend enough time thinking through the composablility side of things.

Just as a side note, on the capturing side, I think perhaps you're misunderstanding my motivation for preventing access at the "wrong time" – maybe I'm way off here, but in my mind preventing accessing variables at the "wrong time" effectively forces the user to immediately capture any variables they plan on using within the hook body, by only allowing them to access the values synchronously ('right at the start of the function'). This means they're forced to "see" those values forever, as you suggest (seeing as any later attempts to read the values would fail with an error). The error throwing is therefore a mechanism that forces users to perform this capturing of immutable values. It could well be me that's misunderstanding your comment though.


Your response has made me think about the tone I've been taking throughout this thread: I owe you and the rest of the team a big apology for throwing round accusations about not listening to users. It must be very frustrating to see people keep coming up with suggestions you've already considered and discarded.

I suspect this is likely happening because (perhaps unusually for this project?) the design process happened behind closed doors, for reasons you discussedin your Reddit comment. This runs counter to most RFC processes I've been involved with, where somebody proposes an idea (perhaps with an initial strawman implementation to guide discussion), then various people chip in with criticisms, suggestions and alternatives, the merits of which are discussed in the RFC thread as the proposed solution gradually mutates into its final state, all alternatives having been discussed at various points throughout the thread. With the hooks API, the first stages of this process seem to have happened internally within Facebook, so for those of us reading this RFC we don't have any knowledge of the rejected prior attempts, so we have no idea that you've already discussed these alternatives at length and already found the flaws in them. If you'd dumped a bunch of gists/sketches of rejected alternatives to accompany the PR (to 'bring the spectators up to speed') there might have been fewer people like me pestering you with the same flawed proposals that you've already examined – although I do realise this involves effort on your part to placate a small minority of people who have problems with the API. If this had just been a feature announcement I would have just moaned a bit to my coder friends and moved on – it was the "RFC" positioning of it that made me expect more of a two-way debate, but that might be my misinterpretation more than anything, and I get that you're too busy keeping the ship afloat to respond in depth to every suggestion.

The reason I got so deeply involved in this thread is that I care intensely about the way React is headed – in large part because React is currently my entire career: I market myself specifically as a 'React Developer' nowadays because I love using it so much that I wouldn't consider a job using any other framework. I still want to be able to call myself a React developer in 5 years' time, and so I really hope that between now and then nothing happens to discourage new users. I've had conversations with various tech decision-makers (mainly from imperative backgrounds) who already struggle to understand the concepts behind React even without hooks, and I hate to think of somebody one day rejecting React for use in a new project on account of it being 'too weird'. Regardless, I'm sure the project is in excellent hands: this is the first API decision I've ever disagreed with in the five years I've been using React, so I'm definitely optimistic in the long run.

In short: I'm sorry for letting my exasperation get the better of me; you're all doing an incredible job, despite people like me making that a very difficult job at times. Hooks are a real game-changer – let's hope that when I'm still a React Developer 5 years from now there's even more cool features that make me love coming into work in the morning 😉

j-f1 reacted with thumbs up emojidanielkcz reacted with laugh emoji

@mindplay-dk
Copy link

@gaearon would you comment onthis approach?

It's not nearly as complete (or pretty) as what@timkendrick posted, but I think I've worked through the most essential parts: state, effects, layout-effects, context and refs, with basic examples of each.

What is the problem or what limitations do you see with this approach?

Maybe point to something tricky you can do with hooks, and I'll see if I can reproduce it with this?

danielkcz reacted with confused emoji

@timkendrick
Copy link

timkendrick commentedMar 29, 2019
edited
Loading

@mindplay-dk it's almost certainly a better approach than my suggestion, but the obvious drawback to me is that you can't depend on prop values in e.g. theuseEffect() handler.

For example, consider the archetypal effect example:

constStatusFeed=component((instance)=>{instance.useEffect(()=>{api.subscribeToFriendStatus(props.friendID, ...);return()=>{api.unsubscribeFromFriendStatus(props.friendID, ...);};});return(props)=> ...;});

The subscription mechanism requires access to currentprops in order to determine the correct ID to fetch, however in your approach you don't have access to props during the hook creation phase.

My first thought to try to work around this would be to store the friendID in some intermediary hook in order to access it within the effect:

constStatusFeed=component((instance)=>{const[friendState]=instance.useState(({ friendID})=>({ friendID}));instance.useEffect(()=>{api.subscribeToFriendStatus(friendState.friendID, ...);return()=>{api.unsubscribeFromFriendStatus(friendState.friendID, ...);};});return(props)=> ...;});

…this feels a little clumsy due to having to introduce a composite mutablestate object just to track a primitive value, but it also doesn't actually solve the problem, seeing as we want the effect to automatically re-run every time the prop changes.

Maybe we could introduce some kind ofuseProps() hook that tracks the current state of the props and can be used as a dependency ofuseEffect:

constStatusFeed=component((instance)=>{constfriendState=instance.useProps(({ friendID})=>friendID);instance.useEffect(()=>{api.subscribeToFriendStatus(friendState.current, ...);return()=>{api.unsubscribeFromFriendStatus(friendState.current, ...);};},[friendState.current]// This won't work);});

…as well as the 'encapsulation-breaking' properties of any potentialuseProps() hook (raised by@gaearon'sresponse to my most recent suggestion), this would of course not work either, seeing as your approach relies on the mutability of thefriendState object returned by the hook. Presumably the factory only runs once, so the dependencies array is incorrect, so this would work for the initial run but would not re-run on subsequent renders if the component is re-rendered with a differentprops.friendID.

I think this exposes a more general problem with effects/callbacks whose implementations depend on the current values of other hooks: how do you write the dependencies array for auseEffect whose callback depends on another hook's current value? One option would be to have the dependencies array be a thunk that you call to get the current values:

constStatusFeed=component((instance)=>{constfriendState=instance.useProps(({ friendID})=>friendID);instance.useEffect(()=>{api.subscribeToFriendStatus(friendState.current);return()=>{api.unsubscribeFromFriendStatus(friendState.current);};},()=>[friendState.current]);return(props)=> ...;});

While this might get round the dependencies array issue, it still relies on the encapsulation-breakinguseProps hook, and I'm worried that any approach that relies on a mutable.current property (or the equivalent mutable.count property in yourcounter.useState() demo) would still be susceptible the 'capturing' concerns raised by@gaearona while back.

As for whether it's a good idea to capture the relevant prop state as a separate hook value in the first place, I actually quite like this, seeing as to my mind it fairly accurately models the fact that the current state is derived from the props (and in retrospect I probably should have used auseProps hook in my suggestion rather than relying on(props) => … functions), however critics might see it as redundant?

Anyway, this is just some stuff to think about – and there's also a good chance I'm misunderstanding your API, so take it with a pinch of salt 😃

@danielkcz
Copy link

danielkcz commentedMar 29, 2019
edited
Loading

@mindplay-dk You can perhaps go throughA Complete Guide to useEffect which depicts thought process behind this in a brilliant way. Try to apply it to your approach if you can achieve similar results.

mindplay-dk and MohamedLamineAllal reacted with thumbs up emoji

@mindplay-dk
Copy link

@timkendrick this is great feedback, thanks :-)

Did you understand that what gets passed to yourcomponent() callback is basically just a regularComponent instance? You should be able to to just access it's props without any further ado:

constStatusFeed=component((instance)=>{instance.useEffect(()=>{api.subscribeToFriendStatus(instance.props.friendID, ...);return()=>{api.unsubscribeFromFriendStatus(instance.props.friendID, ...);};});return(props)=> ...;});

I guess I could pass them to theuseEffect() callbacks for convenience:

constStatusFeed=component((instance)=>{instance.useEffect(({ friendID})=>{api.subscribeToFriendStatus(friendID, ...);return({ friendID})=>{api.unsubscribeFromFriendStatus(friendID, ...);};});return(props)=> ...;});

I'm sort of leaning towards the philosophy of keeping it "just a component" though, and it's perhaps a bit confusing with thefriendID in the inner scope hiding thefriendID from the outer scope? And as said, you have access toinstance.props already, so probably not necessary?

@timkendrick
Copy link

timkendrick commentedMar 29, 2019
edited
Loading

Oh sorry, I didn't realise it was an actualinstance – I assumed it was some kind of context object which you could use almost like a 'placeholder' reference to the component, gotcha.

Is there even such a thing as a component instance with non-class-based-components? I thought that in the world of modern function components, you just have the combination of render function plus a collection of 'memory cells' within the current fiber that store hook state etc, and no actual instance? As far as I was aware the current thinking with the fiber internals is to move away from long-lived component instances towards a more stateless model that presumably is a better fit for async rendering / increased parallelization etc… I could be way off on this though.

Either way, I think it's safe to say I misunderstood your API so my comments don't really apply to it 😃

j-f1 reacted with thumbs up emoji

@mindplay-dk
Copy link

mindplay-dk commentedApr 1, 2019
edited
Loading

@timkendrick I'm less interested in the micro performance details. What I like about hooks is how they enable you tocompose behavior - I don't have any problem with class-based components as such (performance or otherwise) I'm just looking for a practical way to compose components and reuse behaviors and states. My main motivation is there areclearly initialization and rendering phases - I'd like to explore approaches that don't try to force them into a single phase.

I don't think being "stateless" is even a goal (since there is clearly a component state, whether it's stored in a component instance or somewhere else) and I'd like to explore approaches that model that fact, rather than trying to make the APIlook functional by relying on global state, side-effects, counting calls, etc.

I like simple things, that's all :-)

timkendrick, zeoneo, guillaume86, and jeffvandyke reacted with thumbs up emoji

@zeg-io
Copy link

zeg-io commentedMay 15, 2019
edited
Loading

I completely agree with the proposal here:facebook/react#14007 the text of which is below. I didn't see this pushed here as suggested, but the idea of useHooks passing anisMounted flag is a great one and I didn't want to see it just die. To quote from the post from@wesleycho:

Do you want to request a feature or report a bug?

Feature request? This is more to kickstart a discussion.

What is the current behavior?

With the current proposal, with any promise-based function call, useEffect must be used something like so

functionMyComponent(props){const[state,setState]=useState(0);letunmounted=false;useEffect(()=>{myXhrRequest().then(data=>{if(!unmounted){setState(data);}});return()=>{unmounted=true;};});  ...}

This is a fairly awkward pattern for promises, as one needs to add an extra check for mounted state potentially quite divorced from the unmount logic. It should be noted that for subscription-based patterns like with rxjs this is fine, but for promises this stinks and is a bit confusing - this would also get repetitive if there are multiple requests involved.

Ideally the useEffect should make it easier to clean up asynchronous code without imposing on the pattern a user would to carry out async behavior. One manner that could remove the need for the confusing return function is to provide a isMounted helper function as an argument for the function passed into useEffect that returns whether the current component is mounted. It does not solve the verbosity completely, which might be outside the scope of React, but it removes the need for extra boilerplate that arguably hurts the readability.

With this proposal, the useEffect part could be rewritten like this

useEffect(({ isMounted})=>{myXhrRequest().then(data=>{if(isMounted()){setState(data);}});});

Just some thoughts anyhow, I would understand if this proposal is rejected.

ivawzh reacted with thumbs up emoji

@jwipeout
Copy link

I really like the change. It makes for more clear and concise code. I have been trying to see an example of how to replace redux using context and hooks. Can you point me in the right direction? I have read through many articles and looks like everyone is doing it a little different and it is difficult to know, which one is better.

@jakewtaylor
Copy link

jakewtaylor commentedMay 28, 2019
edited
Loading

@zeg-io Couldn't you just make that simple hook yourself and reuse it?

constuseMountedEffect=(callback)=>{letmounted=true;useEffect(()=>{callback({ mounted});return()=>mounted=false;});};// Use:useMountedEffect(({ mounted})=>{myXhrRequest().then(data=>{if(mounted){setState(data);}});});

You could even adjust it so that the callback is only ever called if mounted, like:

useEffect(()=>{if(mounted){callback();}return()=>mounted=false;});
j-f1 reacted with thumbs up emojidkovacevic15, yoieh, and c708423 reacted with thumbs down emoji

@c708423
Copy link

c708423 commentedSep 4, 2019
edited
Loading

@zeg-io I think we can cancel the request inuseEffect return in case cause a memory leak error msg.
If u just want to store state as a component state.

useEffect(()=>{constmyRequest=myXhrRequest().then(data=>{setState(data);});return()=>{myRequest.cancel();}});

If this a reuse state. cache it, or something like global store.

useEffect(()=>{constmyRequest=myXhrRequest().then(data=>{dispatch('updateSomeData',data);// or globalStore.yourData = data;});return()=>{// no need to cancel}});

@lukcad
Copy link

lukcad commentedSep 15, 2019
edited
Loading

Loving this new API, but I have one primary concern - where the hooks come from.

I'm concerned that these hook functions are provided by the top level react module and imply (or require) global state to work. Ideally, hooks and effects functions could be provided to the functional component when it's executed.

So while this example as illustrated by Dan requires from the module:

const { useContext } = require('react')const SomeContext = require('./SomeContext)function Example({ someProp }) {  const contextValue = useContext(SomeContext)  return <div>{someProp}{contextValue}</div>}

Was it considered to pass the hooks into the functional component?

Perhaps looking something like:

const SomeContext = require('./SomeContext)function Example({ someProp }, hooks) {  const contextValue = hooks.useContext(SomeContext)  return <div>{someProp}{contextValue}</div>}

This means functional components which use hooks are(Props, Hooks) => ReactNode. While this is still not "pure" because hooks have effects and are not pure, at least the renderer could provide the appropriate hooks per call-site instead of relying on global state. I could imagine that the Fiber renderer might have different implementation of hooks than some potential future renderer, a server-side renderer, a test renderer with mocked effects, etc.

Thoughts?

// THIS IS FUNCTION BY HOOKfunction Example(){  const [count,setCount] = React.useState(0);  return(    <div>      <b>You clicked this button {count} times.</b> <br />      <button onClick={() => setCount(count + 1)} > Click me </button>    </div>  );}

@czf1998
Copy link

what about this?@pkf1994

@ranneyd
Copy link

@zeg-io Couldn't you just make that simple hook yourself and reuse it?

constuseMountedEffect=(callback)=>{letmounted=true;useEffect(()=>{callback({ mounted});return()=>mounted=false;});};// Use:useMountedEffect(({ mounted})=>{myXhrRequest().then(data=>{if(mounted){setState(data);}});});

You could even adjust it so that the callback is only ever called if mounted, like:

useEffect(()=>{if(mounted){callback();}return()=>mounted=false;});

One issue is youruseEffect does pass[] as the second param, meaning it will run on every re-render, not just when the component unmounts.

This is an issue I've noticed with the "bailout" pattern: I may have auseEffect that depends on several variables. Ionly want it to skip the state setting when the component isunmounted, not every time that particularuseEffect invocation is cleaned up. For instance, I may have auseEffect that refetches data based on user input. However, instead of overwriting the data, it saves it in a keyed object so if they set the settings back, we don't need to refetch. If they change the settings while we're still fetching, I don't want to throw away the results, so I can't use the cleanup function to signify that state change is illegal.

I've done something similar but with a custom ref-based hook:

export const useIsMounted = () => {  const isMountedRef = useRef(true);  useEffect(() => {    return () => {      isMountedRef.current = false;    };  }, []);  const getIsMounted = useCallback(() => isMountedRef.current, []);  return getIsMounted;};

What's cool about this is:

  • getIsMounted is from auseCallback with[] as the second arg, meaning it will never get re-defined. We can safely pass it as an dependency in auseEffect without fear that it will, itself, cause theuseEffect to re-run
  • isMountedRef is a ref as opposed to a state variable, sogetIsMounted doesn't need to specify it as a dependency
  • TheuseEffect has[] as the dependencies, meaning it will only run on mount, meaning the cleanup will only run on unmount.

When using this, you useconst getIsMounted = useIsMounted(); then callgetIsMounted(). Again, it needs to be the function, not the value, so you're getting the value out of the ref and not forcinguseEffect to rerun.

@jsamr
Copy link

jsamr commentedSep 11, 2020
edited
Loading

I have a feeling thatuseEffect is missing an optional third argument, which are the dependencies required to change for the effect to be run. The second "deps" argument might, or might not coincide with the aforementioned argument, depending on component behavior requirements, and sometimes the intersection between those two could be empty. A basic example: I want to scroll to top of a component when the content changes (first dependency), but this effect also depends on a variable padding top (second dep). Currently what I can do is:

functionMyComponent(props){const{ paddingTop, content}=props;constref=React.useRef();React.useEffect(()=>{// scroll to paddingTop when content changes?ref.current.scrollTo(0,paddingTop);},[paddingTop,content]);return<divref={ref}>...</div>}

There is an undesired behavior: the hook is executed on paddingTop changes. Moreover, "content" is not, semantically, a dependency of the callback, but rather a dependency of the "when this side effect should happen". So I could use a ref, store the previous value of paddingTop, and compare the two. But that is cumbersome.What I would like to do, is express the "when this side-effect should happen" dependencies declaratively:

functionMyComponent(props){const{ paddingTop, content}=props;constref=React.useRef();React.useEffect(()=>{// scroll to paddingTop when content changes.ref.current.scrollTo(0,paddingTop);},[paddingTop],[content]);return<divref={ref}>...</div>}

So a new signature foruseEffect would be:

/** *@param what - what this side effect does? *@param whatDeps - which variables modify what the side effect does? *@param whenDeps - which variables modify when the side effect occurs? */typeuseEffect=(what:(...args:any[])=>any,whatDeps:any[],whenDeps?:any[])=>void;

The community seems to be in need of a solution, seehttps://stackoverflow.com/q/55724642/2779871 What are your thoughts?

EDIT-1: added signature.
EDIT-2: Comment here#176

jeffersoneagley, boyanlevchev-pol, and ruofanwei reacted with thumbs up emoji

@gaearon
Copy link
Member

gaearon commentedSep 11, 2020
edited
Loading

If you have ideas, concerns or suggestions, it would be best to file a new issue in the React repo or a new pull request in this repo. We're not tracking old discussions, and this one in particular will email 300 people on every comment.

ConAntonakos, NE-SmallTown, hylerrix, and ludolara reacted with thumbs up emoji

@MingChe0102
Copy link

hook函数 与 hoc 使用的时机是什么时候?有什么区别和意义?

kevin940726, justjavac, lakerswgq, lexsix, MuYunyun, cuizaiyong, blogrocks, nljshoxbb, dvaJi, XQY279, and 7 more reacted with thumbs down emojiyuyezhizhi reacted with laugh emoji

@workspaceking
Copy link

workspaceking commentedApr 4, 2021
edited
Loading

Very exciting, this looks awesome!

Questioning the tuple return type:

const[count,setCount]=useState(0);

Why an array tuple instead of an object containingcount andsetCount members? Is this for performance(and if so, do we have numbers to show browsers or Node behaving strongly differently)? It feels like leaving the returned object open to adding more fields might make it easier to remain backwards compatible in the future, similar to how APIs often end up taking in single objects as a parameter rather than long lists of parameters.

const{ value, set}=useState(0);

Edit: yes, per@jaredLunde's comment, the properties would need constant names.

For React it's easy to get the state and set function from an array, in the case of an object you have to extract keys using Object.keys() then spread the state and set function.

In the case of an array index, 0 is the state and 1 is the function that handles the change in state

trixn86, rkharsan, and Shnnli reacted with confused emoji

Copy link

@ahmed-alsaremahmed-alsarem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Ahmed

tobias-tengler and KrisLau reacted with thumbs down emojidustin-relicx, hekatx, and aviral-spotnana reacted with laugh emoji
@omeid
Copy link

For the better or worst, this is a dead horse. Would one of the maintainers please lock this issue or at least restricted to avoid all the spam.

dustin-relicx, trixn86, SukkaW, MeguminSama, and omidgharib reacted with thumbs up emoji

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers
7 more reviewers

@iossiossioss left review comments

@jamiebuildsjamiebuildsjamiebuilds left review comments

@joepie91joepie91joepie91 left review comments

@RetsamRetsamRetsam left review comments

@KylePalkoKylePalkoKylePalko left review comments

@maciejsikoramaciejsikoramaciejsikora left review comments

@ahmed-alsaremahmed-alsaremahmed-alsarem left review comments

Reviewers whose approvals may not affect merge requirements
Assignees
No one assigned
Labels
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

339 participants
@sebmarkbage@grabbou@pcmaffey@pheuter@JoshuaKGoldberg@jaredLunde@alexeyraspopov@jamesplease@eps1lon@benjamn@maddie927@adamhaile@sophiebits@leebyron@kartikag01@ntucker@david@quantizor@trueadm@pauls-ai2@phil-lgr@ValeriaVG@itsMapleLeaf@mikesteele@fongandrew@netanelgilad@dantman@jordonbiondo@lmatteis@streamich@thisRaptori@emmadal@arianon@jetpacmonkey@pspeter3@jamiebuilds@markerikson@satya164@andrewmcwatters@AlvaroBernalG@contractormario@Michielnuyts@mauriciomelo@aaronjensen@matchu@SethDavenport@theKashey@ezekielchentnik@ianh@SyedFarhan@omeid@pfgray@VinSpee@kevinSuttle@benwiley4000@wheelo@itaylor@MatthewHerbst@intellild@muelvenables@alqamabinsadiq@thysultan@fendy3002@kana-sama@AvraamMavridis@SkReD@hannadrehman@punmechanic@TrySound@newtack@maciejsikora@mateuszwozniak@alexanderby@kay-is@ThePaulMcBride@AnderssonChristian@RichieAHB@jamiewinder@kidandcat@the-spyke@jfo84@hen-x@iy3r@jacobp100@panuhorsmalahti@JCB-K@salvoravida@sebinsua@grncdr@Zacqary@koba04@brucou@danielkcz@jblock@dtinth@aleclarson@vojtech-dobes@zombieJ@octaviogb@gnapse@threehams@Volune@thheller@raibima@zth@cloudever@StephanHoyer@DavidBabel@pmizio@guillaume86@dankreiger@Hypnosphi@PutziSan@pygy@leoyli@philipp-spiess@xialvjun@jomaxx@AjaxSolutions@Havret@gre@HenriBeck@barretron@gleb-lobastov@methyl@kaflan@erik-kallen@n-filatov@gaearon@dwelle@jackkainov@mindplay-dk@drcmda@simon-robertson@thecodecafe@breathe@lilactown@gotbahn@pungggi@j-f1@falconmick@Jessidhia@OEvgeny@damaon@knpwrs@mmmveggies@Billy-@TrevorSayre@shavyg2@bkotrys@ntower@hsavit1@dead-claudia@ChibiBlasphem@dan-lee@aminpaks@darwin-gautalius@sorahn@Mathspy@sarimarton@arieh@KylePalko@localvoid@kerryboyko@lucasconstantino@holywyvern@carlomartinucci@ioss@williamfeng91@icynoangel@wmertens@dntzhang@izanf@Coooooooola@GrzegorzWidla@tranbathanhtung@raunofreiberg@marcelobart@arendjr@scwood@dropbeardan@pleerock@samfrances@WojciechRydel@esnunes@samsch@rajdee@IvanPortfolio@darthtrevino@devantic@adamkleingit@yuyang041060120@ivan-kleshnin@PinkaminaDianePie@KaiSchniggendiller@Lucretius@tldrRD@davidsavoie1@ggassmann@Finesse@ljharb@MarcosApostolo@nasreddineskandrani@strayiker@EECOLOR@rashidul0405@fiture@jimmycallin@jacobbogers@kentcdodds@juanmanuelramallo@jeremytenjo@suchipi@rgdelato@gocanto@eabassey@norbertpy@xari@Janpot@AlexGalays@jbcazaux@cristianoc@mauricedb@jakewtaylor@alexjeffburke@lihroff@jamesgpearce@hanseh25@mcjazzyfunky@ianstormtaylor@mrozbarry@zakjholt@rodrigocfd@JesseChrestler@simmo@onnovisser@nosidotgif@doasync@hamedmam@aknuds1@moaxaca@fortinmike@tim-stasse@andywer@zheeeng@vadzim@buzzpsych@yongzhi-chen@timkendrick@askbeka@Retsam@vadzim-revolist@bpartridge@ngolin@tony-go@hyphaene@benjamin-yayon@aditya02maurya@AndreiCalazans@eric-kimbrel@mizchi@brad-decker@dgreensp@trixn86@peternycander@EdGraVill@brennancheung@transitive-bullshit@interphx@davidnagli@jay51@btraljic@edgesoft@JekaMedvedev@sokra@tcpaul@antmdvs@zouloux@marcelmokos@subhero24@lili21@rajeshdavidbabu@Crecket@dandrei@bjankord@vijayst@ldiego08@LucaColonnello@curran@just214@szoio@atoztoa@favila@HaNdTriX@dmarjenburgh@rwinzhang@ayleesedai@rob2d@lasthrun@timesp@Dudeonyx@ElvenMonky@andrewfluck@aditya81070@KpjComp@cag-oss@ts-23@NateBrady23@mlimberg@amshtemp@spion@amiraliweblite@seahorsepip@divjakLab@doniyor2109@quanganhtran@sponte@fuhaiwei@pixelkritzel@joshburgess@happycollision@dkovacevic15@pupudu@dqunbp@dreina-toast@neerfri@Meligy@zeg-io@jwipeout@c708423@lukcad@czf1998@ranneyd@jsamr@MingChe0102@workspaceking@joepie91@ahmed-alsarem@facebook-github-bot

[8]ページ先頭

©2009-2025 Movatter.jp