Giter Site home page Giter Site logo

rfcs's Introduction

React RFCs

Many changes, including bug fixes and documentation improvements can be implemented and reviewed via the normal GitHub pull request workflow.

Some changes though are "substantial", and we ask that these be put through a bit of a design process and produce a consensus among the React core team.

The "RFC" (request for comments) process is intended to provide a consistent and controlled path for new features to enter the project.

Active RFC List

Contributor License Agreement (CLA)

In order to accept your pull request, we need you to submit a CLA. You only need to do this once, so if you've done this for another Facebook open source project, you're good to go. If you are submitting a pull request for the first time, just let us know that you have completed the CLA and we can cross-check with your GitHub username.

Complete your CLA here.

When to follow this process

You should consider using this process if you intend to make "substantial" changes to React or its documentation. Some examples that would benefit from an RFC are:

  • A new feature that creates new API surface area, and would require a feature flag if introduced.
  • The removal of features that already shipped as part of the release channel.
  • The introduction of new idiomatic usage or conventions, even if they do not include code changes to React itself.

Some changes do not require an RFC:

  • Rephrasing, reorganizing or refactoring
  • Addition or removal of warnings
  • Additions that strictly improve objective, numerical quality criteria (speedup, better browser support)
  • Additions only likely to be noticed by other implementors-of-React, invisible to users-of-React.

What to expect

It is hard to write an RFC that would get accepted. Nevertheless, this shouldn't discourage you from writing one.

React has a very limited API surface area, and each feature needs to work seamlessly with all other features. Even among the team members who work on React full time every day, ramping up and gaining enough context to write a good RFC takes more than a year.

In practice, React RFCs serve two purposes:

  • React Team RFCs are submitted by React Team members after extensive (sometimes, multi-month or multi-year) design, discussion, and experimentation. In practice, they comprise the majority of the RFCs that got merged so far. The purpose of these RFCs is to preview the design for the community and to provide an opportunity for feedback. We read every comment on the RFCs we publish, respond to questions, and sometimes incorporate the feedback into the proposal. Since our time is limited, we don't tend to write an RFC for a React feature unless we're very confident that it fits the design. Although it might look like most React Team RFCs easily get accepted, in practice it's because 98% of ideas were left on the cutting room floor. The remaining 2% that we feel very confident and have team consensus on about are the ones that we announce as RFCs for community feedback.

  • Community RFCs can be submitted by anyone. In practice, most community RFCs do not get merged. The most common reasons we reject an RFC is that it has significant design gaps or flaws, does not work cohesively with all the other features, or does not fall into our view of the scope of React. However, getting merged is not the only success criteria for an RFC. Even when the API design does not match the direction we'd like to take, we find RFC discussions very valuable for research and inspiration. We don't always review community RFCs in a timely manner, but whenever we start work on a related area, we check the RFCs in that area, and review the use cases and concerns that the community members have posted. When you send an RFC, your primary goal should not be necessarily to get it merged into React as is, but to generate a rich discussion with the community members. If your proposal later becomes accepted, that's great. But even if it doesn't, it won't be in vain. The resulting discussion often informs the next proposal in the same problem space, whether it comes from the community or from the React Team. Many library authors are reading the discussions, so RFCs often lead to community experimentation and userland solutions.

We apply the same level of rigour both to React Team RFCs and Community RFCs. The primary difference between them is in the design phase: React Team RFCs tend to be submitted at the end of the design process whereas the Community RFCs tend to be submitted at the beginning as a way to kickstart it.

What the process is

In short, to get a major feature added to React, one usually first gets the RFC merged into the RFC repo as a markdown file. At that point the RFC is 'active' and may be implemented with the goal of eventual inclusion into React.

  • Fork the RFC repo http://github.com/reactjs/rfcs
  • Copy 0000-template.md to text/0000-my-feature.md (where 'my-feature' is descriptive. Don't assign an RFC number yet).
  • Fill in the RFC. Put care into the details: RFCs that do not present convincing motivation, demonstrate understanding of the impact of the design, or are disingenuous about the drawbacks or alternatives tend to be poorly-received.
  • Submit a pull request. As a pull request the RFC will receive design feedback from the larger community, and the author should be prepared to revise it in response.
  • Build consensus and integrate feedback. RFCs that have broad support are much more likely to make progress than those that don't receive any comments.
  • Eventually, the team will decide whether the RFC is a candidate for inclusion in React. Note that a team review may take a long time, and we suggest that you ask members of the community to review it first.
  • RFCs that are candidates for inclusion in React will enter a "final comment period" lasting 3 calendar days. The beginning of this period will be signaled with a comment and tag on the RFCs pull request.
  • An RFC can be modified based upon feedback from the team and community. Significant modifications may trigger a new final comment period.
  • An RFC may be rejected by the team after public discussion has settled and comments have been made summarizing the rationale for rejection. A member of the team should then close the RFCs associated pull request.
  • An RFC may be accepted at the close of its final comment period. A team member will merge the RFCs associated pull request, at which point the RFC will become 'active'.

The RFC lifecycle

Once an RFC becomes active, then authors may implement it and submit the feature as a pull request to the React repo. Becoming 'active' is not a rubber stamp, and in particular still does not mean the feature will ultimately be merged; it does mean that the core team has agreed to it in principle and are amenable to merging it.

Furthermore, the fact that a given RFC has been accepted and is 'active' implies nothing about what priority is assigned to its implementation, nor whether anybody is currently working on it.

Modifications to active RFCs can be done in followup PRs. We strive to write each RFC in a manner that it will reflect the final design of the feature; but the nature of the process means that we cannot expect every merged RFC to actually reflect what the end result will be at the time of the next major release; therefore we try to keep each RFC document somewhat in sync with the language feature as planned, tracking such changes via followup pull requests to the document.

Implementing an RFC

The author of an RFC is not obligated to implement it. Of course, the RFC author (like any other developer) is welcome to post an implementation for review after the RFC has been accepted.

If you are interested in working on the implementation for an 'active' RFC, but cannot determine if someone else is already working on it, feel free to ask (e.g. by leaving a comment on the associated issue).

Reviewing RFCs

Currently, the React Team cannot commit to reviewing RFCs in a timely manner. When you submit an RFC, your primary goal should be to solicit community feedback and generate a rich discussion. The React Team re-evaluates the current list of projects and priorities every several months. Even if an RFC is well-designed, we often can't commit to integrating it right away. However, we find it very valuable to revisit the open RFCs every few months, and see if anything catches our eye. Whenever we start working on a new problem space, we also make sure to check for prior work and discussion in any related RFCs, and engage with them.

We read all RFCs within a few weeks of submission. If we think the design fits React well, and if we're ready to evaluate it, we will try to review it sooner. If we're hesitant about the design or if we don't have enough information to evaluate it, we will leave it open until it receives enough community feedback. We recognize it is frustrating to not receive a timely review, but you can be sure that none of the work you put into an RFC is in vain.

Inspiration

React's RFC process owes its inspiration to the Yarn RFC process, Rust RFC process, and Ember RFC process.

We've changed it in the past in response to feedback, and we're open to changing it again if needed.

rfcs's People

Contributors

acdlite avatar adreesdev avatar akintoluvic avatar ashtonsix avatar bvaughn avatar cybai avatar dps0340 avatar facebook-github-bot avatar gaearon avatar gnoff avatar henriquelimas avatar hyeoke avatar j4chou avatar josephsavona avatar jyash97 avatar pengjiyuan avatar petetnt avatar rickhanlonii avatar ryo-manba avatar sebmarkbage avatar shahrzad-gh avatar sophiebits avatar styfle avatar trysound avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rfcs's Issues

[Hooks] useComponent - alternative to return

One issue with hooks is that useEffect happens after return (i.e. render), but it must come first in code order by virtue of how functions work.

It would be nice to optionally omit the return while introducing a useComponent hook that can be included anywhere within a component body.

function DisplayCount(props) {
  const [count] = useState();

  useComponent(<div>{count}</div>);

  useEffect(()=> {
    doSomethingAfterRender();
  });
}

This precludes us from branched rendering (aside from inline statements), but hopefully conditional blocks come to hooks one day.

[Contexts]to control the render of Consumer Component

In the Context.Provider, there is a word as follow:

All consumers that are descendants of a Provider will re-render whenever the Providerโ€™s value prop changes. The propagation from Provider to its descendant consumers is not subject to the shouldComponentUpdate method.

It says the Consumer Component will be updated when the value in the Provider Component changes. I understand the reason why the shouldComponentUpdate or PureComponent not effect in the Consumer Component. In my option, there should still be a method to replace the shouldComponentUpdate to control the reRender of the Consumer Component.

[Proposal] + ReactDOM.hydrateElement

A colleague at work recently turned me onto Vue. So I read "The Majesty of Vue 2".

In it, there is a project where you use Vue for a search bar and results.

After learning Vue, I looked to see if React had any functionality like it. The closest I could find is React Hooks.

Below are pictures of a search bar with results (and their respective code bases) rendered with React and with Vue.

react hooks vue rendered

react hooks vs vue

In a nutshell, the one use case where Vue beats React is SSR because in order to make that work in React, one needs to setup a Node server.

What I am proposing is a Vue-inspired work around. react-dom already provides the functions render + hydrate, which replace a DOM node with one generated by React.createElement

What if instead of replacing an entire node, you could simply inject JavaScript into it?

ReactDOM.hydrateElement(propertiesObject, domNode)

So, for example, instead of:

<div id="react-storyboard"></div>

and

render( <Storyboard users={reactUsers}/>, document.getElementById("react-storyboard") )

I could do

<input id="search-bar"
       type="text"
       placeholder="Search for stories... with React Hooks!"
       value="">

<ul id="search-results">
  <!-- using real data, the server-side could display data here -->
</ul>
let users = API.getUsers(),
    [query, setQuery] = useState(""),
    [data,  setData]  = useState(users)

function handleChange(event){
  setQuery(event.target.value)

  let newData = {}
  for (const key in users) {
    newData[key] = users[key].filter(
                     story => story.includes(event.target.value)
                   )
  }

  setData(newData)
}

const SearchResults = ({data}) =>
  <ul>{Object.entries(data).map(([name, stories]) =>
    stories.map((story, index) =>
      <li key={index}>
        {name} posted "{story}"
      </li>
    )
  )}</ul>

ReactDOM.hydrateElement({value: query, onChange: handleChange}, document.getElementById("search-bar"))
ReactDOM.render(<SearchResults data={data}/>, document.getElementById("search-results"))

In this way, I no longer have to duplicate an <input> and include an extra <div> to get SSR.

cc: @gaearon

@timkendrick I'm less interested in the micro performance details. What I like about hooks is how they enable you to *compose 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 are *clearly* initialization and rendering phases - I'd like to explore approaches that don't try to force them into a single phase.

@timkendrick I'm less interested in the micro performance details. What I like about hooks is how they enable you to compose 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 are clearly 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 API look functional by relying on global state, side-effects, counting calls, etc.

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

Originally posted by @mindplay-dk in #68 (comment)

Async render generator

This is not a proposal per se, just wanted to see your thoughts on async generator as a render function.

Stateless component:

async function * MyComp () {
  yield (<div>Loading...</div>);
  const users = await fetch('/users');
  return <UserList users={users} />;
}

Stateful component:

class MyComp extends Component {
  async * render () {
    yield (<div>Loading...</div>);
    const users = await fetch('/users');
    return <UserList users={users} />;
  }
}

What it solves?

  1. Async data loading.
  2. Async data loading on server side.

On server side it could render only once the iterator has been fully resolved, while on client it would render all intermediate steps of the iterator.

For example, on client it would render:

  1. <div>Loading...</div>
  2. <UserList users={users} />

On server it would wait until render method fully resolves and render the final result only:

  1. <UserList users={users} />

[Hooks] Alternative state

I would prefere this
Language-level syntax state foo = 5;
rather than const [foo, setFoo] = useState(5);

The latter feels so cumbersome.

[Hooks] useSuspense

In some cases, a Suspense must be declared in a parent component, that makes a more division of component tree

function SuspensibleDataSourceContainer() {
   return (
     <React.Suspense fallback="loading">
       <DataSourceContainer />
     </React.Suspense>
   )
}

Maybe add useSuspense that would do the same thing as catch a thrown promise?

function DataSourceContainer() {
   // declare before
   useSuspense(FallbackComponent, maxDuration);
   // call resource fetcher
   const data = resource.read();

   return <DataViewComponent {...data} />
}

This way one intermediary component SuspensibleDataSourceContainer might be avoided.

Contribution Guide/Syntax Preferences

Looking over the Context proposal (#2 ), it's using ~~~Stage-2~~~ Stage-3 ES and Flow typings, which are great, but for someone not familiar with Flow a bit hard to follow in parts. However, React's codebase does use Flow, so it might be considered fair game. We've added syntax guidelines to the docs already, do you think something like that would be warranted for the RFCs, too?

How can we fetch data async in reaction to props changes with getDerivedStateFromProps?

I couldn't find this mentioned in the RFC.
Currently almost all of our components implement compoentWillReceiveProps to trigger some async operation in reaction to props changes, then call setState asynchronously. Simple example:

componentWillReceiveProps(newProps) {
  if (this.props.userID !== newProps.userID) {
    this.setState({ profileOrError: undefined })
    fetchUserProfile(newProps.userID)
      .catch(error => error)
      .then(profileOrError => this.setState({ profileOrError }))
  }
}

I.e. the new derived state from the Props is not derived synchronously, but asynchronously. Given that getDerivedStateFromProps is sync, I don't see how this could work with the new API.

Note that the above example is very simple, is prone to race conditions if the responses arrive out of order and does no cancellation on new values or component unmount.

We usually make use of RxJS Observables to solve all those issues:

private propsUpdates = new Subject<Props>()
private subscription: Subscription

componentDidMount() {
  this.subscription = this.propsUpdates
    .pipe(
      map(props => props.userID),
      distinctUntilChanged(),
      switchMap(userID => fetchUserProfile(userID).pipe(catchError(error => [error])))
    )
    .subscribe(profileOrError => {
      this.setState({ profileOrError })
    })
  this.propsUpdates.next(this.props)
}

componentWillReceiveProps(newProps) {
  this.propsUpdates.next(newProps)
}

componentDidUnmount() {
  this.subscription.unsubscribe()
}

So now that the methods we were making use of are deprecated, I wonder were we doing this wrong all the time? Is there already a better way to do this with the old API? And what is the recommended way to do this with the new API?

[Hooks] useEffect infinite loop

I managed to create an infinite loop with useEffect when synchronizing URL with a local route state when preparing an example for a workshop.

Full (fixed) code is on https://github.com/msd-code-academy/react-workshop/blob/76d381db8f44ec2a89ed96b22035552e70ef4bf5/00-props-state/search-example/src/App.js , the main part is:

const getHash = () => window.decodeURI(window.location.hash.substr(1))

const App = () => {
  const [route, setRoute] = useState(getHash())

  useEffect(() => {
    window.location.hash = route
  }, [route])

  useEffect(() => {
    window.onhashchange = () => setRoute(getHash())
    return () => window.onhashchange = undefined
  }, [])
  ...

I did not save the version with an infinite loop (sorry, I had a deadline), but my browser became completely unresponsive and I was not able to close the tab or switch to other tabs, only to kill whole browser (and send a crash report to Firefox and then the restored session was loaded with old code so I had to kill it again without restoring the session).

Maybe there is a safer pattern for 2-way synchronization of internal React state with external resources (feel free to comment), but I wonder if it would be possible to somehow detect an infinite loop and prevent a never ending re-rendering, or some other way to allow the browser to be responsive for tab closing / hot reloading of a fixed version?

Perhaps debouncing the useEffect or setStateX to 10ms would help, but I have no idea about wider implications (e.g. 60fps animations), nor how often it was actually called (no dev tools in the unresponsive browser)...

Edit: I was unable to reproduce with the following simplified version (the page does not freeze), must be something about window.location I guess:

  const [count, setCount] = React.useState(0)
  React.useEffect(() => setCount(count + 1), [count])

[Hooks] useSharedRef(Symbol)

Since hooks are extremely isolated it is impossible to make some hooks act differently depending on if another hook has been previously used on the same component or not.

While this might be easy to pull off when the userland is completely controlled (for example by getting a value from hook 1 and passing it to hook 2) it becomes impossible when it is not.

Imagine this use case (1):

  • A library offers two hooks
  • They can be used separately, but not together
  • There's no way for the hooks to know when this happens

Another use case (2):

  • A hook is supposed to be used only once per component
  • How?

A way to be able to do this could be using a
const data = useSharedRef(Symbol)
Where the data only is shared when inside the same component.

Use case 1:

const hook1UsedSymbol= new Symbol();
const hook2UsedSymbol= new Symbol();

function useHook1() {
  const hook1Used = useSharedRef(hook1UsedSymbol);
  const hook2Used = useSharedRef(hook2UsedSymbol);
  useMemo(() => {
    if (hook2Used) {
      throw new Error('hook 1 incompatible with hook 2')
    }
    hook1Used.current = true;
  }, []);
}

function useHook2() {
  const hook1Used = useSharedRef(hook1UsedSymbol);
  const hook2Used = useSharedRef(hook2UsedSymbol);
  useMemo(() => {
    if (hook1Used) {
      throw new Error('hook 2 incompatible with hook 1')
    }
    hook2Used.current = true;
  }, []);
}

Use case 2:

const hookUsedSymbol= new Symbol();

function useHook() {
  const hookUsed = useSharedRef(hookUsedSymbol);
  useMemo(() => {
    if (hookUsed) {
      throw new Error('hook can only be used once')
    }
    hookUsed.current = true;
  }, []);
}

Testing functional components using state hooks with Enzyme

describe('App', () => {
  test('renders one span element with the name as text', () => {
    const component = shallow(<App />); // Enzyme's shallow not working with React state hooks
    expect(component.find('span')).toBeTruthy();
    expect(component.find('span').text()).toBe('Dan');
  });
});

The error looks like this if I shallow render:

 FAIL  src/components/App/App.test.js
  App
    โœ• renders one span element with the name as text (16ms)

  โ— App โ€บ renders one span element with the name as text

    Invariant Violation: Hooks can only be called inside the body of a function component.

       6 |
       7 | export default function App() {
    >  8 |   const [name, setName] = useState('Dan');
         |                           ^
       9 |   return (
      10 |     <span>{name}</span>
      11 |   );

      at invariant (node_modules/react/cjs/react.development.js:125:15)
      at resolveDispatcher (node_modules/react/cjs/react.development.js:1450:28)
      at useState (node_modules/react/cjs/react.development.js:1473:20)
      at App (src/components/App/App.js:8:27)
      at node_modules/enzyme-adapter-react-16/build/ReactSixteenAdapter.js:436:38
      at ReactShallowRenderer.render (node_modules/react-test-renderer/cjs/react-test-renderer-shallow.development.js:450:39)
      at node_modules/enzyme-adapter-react-16/build/ReactSixteenAdapter.js:440:37
      at withSetStateAllowed (node_modules/enzyme-adapter-utils/build/Utils.js:132:16)
      at Object.render (node_modules/enzyme-adapter-react-16/build/ReactSixteenAdapter.js:439:70)
      at new ShallowWrapper (node_modules/enzyme/build/ShallowWrapper.js:204:22)
      at shallow (node_modules/enzyme/build/shallow.js:21:10)
      at Object.test (src/components/App/App.test.js:7:23)

The component looks like this:

export default function App() {
  const [name, setName] = useState('Dan');
  return (
    <span>{name}</span>
  );
}

Is this an issue Enzyme will have to deal with or is there some other underlying issue?

The test passes with a mount render, but in a simple component test like this I would usually do a shallow render.

I know this might be outside of the scope of this thread, but at the same time I love using the Enzyme test utilities, and I thought I should bring this up.

Originally posted by @dankreiger in #68

[Hooks] getState

I'm pretty hyped about hooks already. I like concept as it respects SOLID better than the current implementation.

It would be great to have a getState method on hooks though.

Syntax

const [value, setValue, getValue] = useState(0);

But, what for
I'm not sure if this is an edgecase and even worth bothering.
But there are a few cases you'd like to have a getState method that does not mutate.

1. Objects / Browser APIs
Imagine you want to create a class that need a handler injected by the constructor.
Let's take ResizeObserver as an example.

const [value, setValue] = useState(0);
new ResizeObserver(() =>{ /*This code is executed everytime an observed element resizes*/ 
    doMagic(value)
} );

The resizeObserver could be stored in the state and effects could be used to observe and unobserve DOM elements. The problem is that the handler you inject does not know about state updates. The variable still points to the same value in the memory.

Well, there's always a workaround. You could store an object in the state and then pass the reference into the handler and always get the current value but this just doesn't feels right.

 const [width, setWidth] = useState(0);
  const [height, setHeight] = useState(0);
  const [shared] = useState({
    width,
    height
  });
  shared.width = width;
  shared.height = height;

  function resizeHandler(entries) {
       // here you always get the current values with shared.width and shared.height
  }

Here's an implementation of an resizeObserver hook.
https://codesandbox.io/s/l9q7ln6lm7

2. Event Handlers
With React.memo() we get the ability to reduce unnecessary re renders. Still a problem are event handlers.

   function Sample(props) {
      const [count, setCount] = useState(0);
       
       const increment = (e) => setCount(count+1);
       return <HeavySubComponent onClick={increment} />
  }

In this case the HeavySubComponent gets rerendered always. Event though, only a event handler changed.

Here's an example:
https://codesandbox.io/s/1z45wml3w7

With memorizing the method we could solve this problem.

  function useEventHandler(handler) {
     const memoizedHandler = memoizeOne((...args) => handler(...args));
      const [handler, setHandler] = useState(memoizedHandler );
  }

   function Sample(props) {
      const [count, setCount] = useState(0);
       
       const increment = (e) => setCount(count+1);
       return <HeavySubComponent onClick={increment} />
  }

here's an example. I mocked the behavior of getState.
https://codesandbox.io/s/2zp01w0x2p

Focus management RFC

This is an RFC for an RFC (ooh so meta ๐Ÿคฏ). I'd like to get feedback from the React core team and the community on whether this is something that React should even handle before I do the work to come up with an API and write a formal RFC. Please let me know what you think!

Focus management

Focus management is the programmatic movement of keyboard focus in an application in response to user input, such as mouse, touch, or keyboard interactions. Implementing keyboard support for components and applications is imperative for accessibility, and not enough web applications implement this properly today. See the ARIA Practices document for more information about focus management and keyboard interfaces.

In React, implementing many common components is very easy, but implementing those components with proper support for accessibility and focus management is still quite difficult. Applications that do implement support typically use a component library which has taken the time to get this right. However, implementing such a library correctly is very challenging and time consuming, and I think React could enable library authors to handle focus management in an easier way.

Challenges

I work on the React implementation of our design system at Adobe. One of our main jobs is to implement a component library with proper support for accessibility, and we have faced some challenges while trying to achieve this goal.

Refs Everywhere

Refs are meant as an escape hatch from the declarative React model to make things happen imperatively, but we've ended up having one or more in almost every component in our library. The main reason we have refs is for focus management. We need to focus particular elements in response to events like keyboard navigation with arrow keys, etc. In addition, sometimes we need to find children of refs with querySelector and other DOM methods in order to focus the correct elements, e.g. elements inside child components. This feels pretty unclean, and un-reacty to us and I think there could be a more declarative API that React could enable.

Global focus state

The imperative focus management API in the DOM is global: only one element can be focused at a time. Calling node.focus() immediately causes all other nodes to lose focus, and the new node to gain focus. Therefore, components basically fight one another for focus. In reusable components, this is a challenge since they don't know what other components are on a page. There is no way for a component to "remember" what was focused before, so that focus can be restored to it later without some manual work in every component. This will be described more below in some of the other sections. I think React components could potentially do this automatically.

Roving tab index

Keyboard users typically navigate between components with the tab key, which changes the focused element. However, composite components such as lists, grids, trees, etc. should only appear in the tab sequence in an application once. It would be annoying if you had to tab through every item in a long list just to get past the entire list to the next element afterward. Therefore, you tab to the list, and if you want to navigate within the list you use the arrow keys, and otherwise you can tab to the next element after the entire list.

The roving tab index pattern is one way to solve this problem, but basically it involves each component being able to "remember" what was focused last, so that if a user tabs away from a list and then back, the list item they were on before is re-focused rather than the first item or the list itself. I think a focus management API in React could potentially help make this problem easier to solve.

Focus Trapping

Focus trapping is important for modals and other popups. It prevents users from tabbing or focusing elements outside a particular region. For example, if a user tabs to the last element in a modal dialog, when they hit tab again the first element of the dialog should be focused instead of something outside. This is currently impossible to implement in a general way in React without manual DOM querying and imperative focusing. I think a focus management API in React could help solve this.

Restoring focus

When you close a dialog or popover, focus should be restored to whatever had focus before the modal opened. This requires us to remember what was focused last, and when the component unmounts focus that element again. I think this should also happen automatically.

Why should this be in React?

We have experimented with various abstractions to make the above easier in user space, but have come to the conclusion that it would be better implemented in React itself. I haven't come up with an exact API that I think React should adopt yet (the purpose of this issue is to get feedback first), but generally, I think components should have a notion of whether they are in focus, and should be able to declaratively specify which of their child elements should have focus, and lock focus to within the component subtree. This has turned out to be difficult to implement in user space without access to the full React tree (only direct component children).

Most of the above points talked about the web, but I think an API in React core for this could be useful for other targets as well, such as React Native. React Native also has imperative APIs for focus management at the moment I think, so perhaps this could be valuable there as well.

Additionally, making implementation of proper accessibility patterns much easier for the entire React community could have a huge effect on the quality of modern web applications. Not everyone is using a component library that has invested as much time as we have into supporting accessibility. If more of this happened in React itself, then there would be less work for library authors and more people would be able to put in the effort.

Thoughts?

Sorry for the long issue. If you have thoughts or feedback please let me know. Is this something the React team and community would be interested in? If so, I'll work on an RFC for an API. If anyone else has ideas or has experience with this, please tell me in the comments!

Negated Boolean Prop Shorthand

JSX has a shorthand boolean prop notation, which sets prop to true.

<input disabled />
<Button small primary ghost disabled block>Click me!</Button>

But this feature allows to set props only to true. What do you think about a shorthand
for setting a prop to false? Like, this is my use case:

<Markdown !escapeHtml source={/*...*/} />

It is useful for components which have props that default to true.

[hooks] useState - "setState" callback

Hi, I'm very hyped about the hooks api and I started experimenting with it on some small side project.

I've noticed that the "setState" function returned by the useState hook doesn't have an "afterRender" callback (the second argument of this.setState in classes)

I didn't find further informations about this in the doc. Is there any design reason why the "afterRender" callback is not present? Is there a plan to introduce it later on?

The reason I ask is that during the past years of react usage I found a few scenarios (very rarely though) were not having the "afterRender" callback would result in a quite tedious implementation of componentDidUpdate or pushed me to change the shape of my state to work around it (I can provide examples about this)

In the meanwhile here the snippet I used to understand that no "afterRender" callback is used by the "setState" of hooks

Counter.js

import React, {useState} from 'react';

export const defaultState = {count: 0}

export const useCounter = () => {

  const [state, setState] = useState(defaultState)

  return {
    ...state,
    onIncreaseCounter () {
      setState({...state, count: state.count + 1}, () => {console.log('++++++++++++++++++')})
    },
    onDecreaseCounter () {
      setState({...state, count: state.count - 1}, () => {console.log('++++++++++++++++++')})
    }
  }

}

export default function Counter ({count, onIncreaseCounter, onDecreaseCounter}) {
  return <div>
    {count}
    <button onClick={onIncreaseCounter}>+</button>
    <button onClick={onDecreaseCounter}>-</button>
  </div>
}

Root.js

import React from 'react';

import Counter, {useCounter} from "./Counter";

export default function Root () {
  return <Counter {...useCounter()}/>
}

the log never appears in the console after after increasing and decreasing the counter

[Hooks] useContext derivation performance issue

A number of libraries currently using HOCs that provide a subset or something derived from context cannot be efficiently ported to hooks because they only use a portion of the context.

When the component they wrap is a PureComponent, as a HOC these libraries only trigger updates when the data they send through props changes. However if they were to use useContext this would result in the component being rendered on every context change, even if the returned data is memoized and no re-render is needed. If the overall context changes frequently, this would mean regular unwanted re-renders.

This includes HOCs like react-redux's connect and Material UI's withStyles.

I think this could be solved by allowing useContext to accept a second argument:

const data = useContext(Context, transformFunction);

The second argument to useContext would be a function that would accept the context value and return another value based on it which would be the return value of useContext (i.e. the default when not specified would essentially be data => data).

React would be able to use this function on context changes. If the context changes but other state/props have not changed React could pre-execute the transformFunction and if the return value has not changed could skip the render. At the very least if the component uses React.memo.

I do recommend treating the transformFunction similar to an effect, i.e. allowing hooks to be used in it. So that it's possible to use useMemo inside the function.

ref methods for function components

Next-gen React is looking great, and class components are now rarely necessary.. but components that offer ref instance methods are still stuck with classes. Here's an example class which I believe is currently impossible to convert to a function component:

class MyInput extends React.Component {
  // how would 'focus' be defined if MyInput was a function?
  focus = () => {
    doSideEffect()
    this.textInput.focus()
  }
  render() {
    return (
      <TextInput
        ref={ti => { this.textInput = ti; }}
        {...this.props}
      />
    );
  }
}

Note: I know refs+methods aren't ideal for most use-cases, but they are a reality that prevents us from leaving behind classes in our codebase.

So, I wrote the above issue and then realized that forwardRef could be used for this:

const MyInput = forwardRef((props, ref) => {
  const innerInput = useRef(null);
  function focus() {
    doSideEffect();
    innerInput.current && innerInput.current.focus();
  }
  ref.current = { focus };
  return <Input {...props} ref={innerInput} />;
});

see runnable example

At first glance, this seems to work. Is this sane? Should this be documented? Optimized? Discouraged?

useAdopt

I am wondering if the below described behaviour is already possible to achieve with existing React hooks, if yes, please tell me how.

I thinking it would be nice to have a React hookโ€”say useAdoptโ€”that would render a React element into the current component.

For example, here could be a use case for useAudio hook:

const Comp = () => {
  const [element, state, actions] = useAudio();

  return (
    <>
       {element}
       <div>Audio is playing: {state.isPlaying}</div>
    </>
  );
};

(Above element renders <audio> tag.)

Instead above example could be rewritten using useAdopt hook:

const Comp = () => {
  const [element, state, actions] = useAudio();
  useAdopt(element);

  return (
     <div>Audio is playing: {state.isPlaying}</div>
  );
};

So, basically all that useAdopt does is inserts a fragment before the returned JSX of the component. (Obviously, useAdopt could have been used internally in useAudio itself, so that user does not have to use it.)

This could be useful to convert numerous render-prop components we have to hooks. For example, let's say you have an <Animation/> FaCC.

const Comp = () => {
  return (
     <Animation from={0} to={100}>{(value) =>
       <Ball x={value} />
     }</Animation>
  );
};

Using useAdopt hook this FaCC could be converted to a hook:

const useAnimation = (from, to) => {
  const [value, set] = useState(from);
  useAdopt(<Animation from={from} to={to}>{(value) => {
    set(value);
    return null;
  }}</Animation>);
  return value;
};

const Comp = () => {
  const value = useAnimation(0, 100):
  return (
     <Ball x={value} />
  );
};

Then in user-space we can create useFaCC for example:

const useFaCC = (element) => {
  const [value, set] = useState();
  const el = React.cloneElement(element, {
    children: (value) => {
      set(value);
      return null;
    }
  });
  useAdopt(el);
  return value;
};

const Comp = () => {
  const value = useFaCC(<Animation from={0} to={100} />):
  return (
     <Ball x={value} />
  );
};

[0006-static-lifecycle-methods] How does getDerivedStateFromProps access the current props?

state-derived-from-propsstate

In Before example, it uses this.props.someValue. But in After, it uses prevState.someMirroredValue.

I do not understand how the this.props(current props) can be accessed in the new static method getDerivedStateFromProps

Here are my questions based on the sample code

  1. Does it mean we need to put the props in the state in every stage?
  2. We are going to return a new state rather than call this.setState. After that, this.state will have the props. If my understanding is correct, do you feel odd because of the duplicated info.
  3. Sometime, I do not want to put everything into the state except something will affect the component rendering. For example, I will store some pre-computed array results as the data member in the React.Component class. There will be an attribute, index in the state for accessing this array. Currently, componentWillReceiveProps is the place for recomputing part of the results. With the static method getDerivedStateFromProps, where should I put this logic?

Foreign renderer

ReactDOM.render(
  <div>
    {/* For example: */}
    {ReactCanvas.render(/* ... */)}
    {ReactDOM.render(/* ... */)}
  </div>
)

What it solves?
Contents that need renderers other than DOM, such as Charts.

Give a `ssrReconciliation={true}` attribute for the developer to ask React to patch up the rendered content if they are not identical between the server and the client

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

feature

What is the current behavior?

React expects that the rendered content is identical between the server and the client. If you intentionally need to render something different on the server and the client, you have to do a two-pass rendering like facebook/react#8017 (comment)

If an attribute is derived from screen size, since we don't know the real screen size in the server side, we might do a 2 pass rendering like following:

constructor(props) {
  super(props)
  this.state = { hasMounted: false }
  
  this.styles = Object.defineProperties({}, {
    'container': { get: _=> {
       if (typeof window == 'undefined') return { height: 300 }
       let height = this.props.viewsize.height * 0.67
       return this.state.hasMounted ? { height } : { height: height - 1 }
    }}
  })
}

componentDidMount() {
    this.setState({ hasMounted: true })
}

render() {
  let s = this.styles
  return (
    <div  style={s['container']}>
  </div>
  )
}

In the above code, I demonstrate that I have to intentionally render a wrong height initially in the browser so I can update it in the second run, or the DOM will keep inconsistent with the vDOM.

This is because if the 2 renderings keep unchanged in vDOM, React won't update the DOM.

Drawbacks

  1. Introduce unnecessary 2nd rendering

  2. The component will do 2 pass rendering in every mounting even it is not after SSR.

What is the expected behavior?

Design a ssrReconciliation={true} attribute, or something like that, e.g. ssrPatchUp={true} for the developer to ask React to patch up if a single element's attribute or text content is unavoidably different between the server and the client when hydrating ( Only after SSR ).

And if it is lucky the rendered content is identical between the server and the client, it will do nothing.

[Hooks] `useError` to enable ErrorBoundary in async functions

A useError would be nice; Can also be created in user-space as https://codesandbox.io/s/mzppq9j30y (close the error-overlay - that is because of react-scripts)

Interface could be

function Page() {
  // named `dispatchError` since `throw` is a JS reserved keyword :(
  const [dispatchError] = useError();

  useEffect(() => {
    setTimeout(() => {
      dispatchError(new Error("Error!"));
    }, 1000);
  });

  return <div>Hello world</div>;
}

which would bobble up to the wrapping ErrorBoundary.

Reason for a "native" useError vs user-spaced
If we have a component which have multiple effects, and one of the effect throws, then a re-render might cause a effect being invoked even if the component will fail;

function Page() {
  const [ data, setData ] = useState();

  // We want this effect to be invoke on every render
  useEffect(() => fetchData('data.json').then(x => setData(x));

  // named `dispatchError` since `throw` is a JS reserved keyword :(
  const [dispatchError] = useError();

  useEffect(() => {
    setTimeout(() => {
      dispatchError(new Error("Error!"));
    }, 1000);
  });

  return <div>Hello world</div>;
}

It would however be fixed by moving the useError to the top of the component, but would prevent "shared" hooks

new 'affect' API

Feature Request

The new Hooks API is awesome. It had me thinking..

One of the issues I have had with React is with render prop based components that pass data to their children. Often I want to be able to control when props are "effectively complete" from a container before telling a child component to render usable data. Until the parent component determines it is complete the child component would remain in a loading state. This loading state can be set by default and updated in its own hooks / CDU / CDM as usual.

What I would propose as a possible solution to this is a modification to the render props pattern where a new API could be put in place on components to conditionally restrict them from passing props down to the child tree. This API would be similar to the new effect API but on the downstream updates. It could be part of the hooks API but have the methods be called "affects".

This API could have a similar footprint where "affects" could be skipped like "effects" can:

https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects

A second argument to the "affect" function could be an array of keys that determine if the props should be passed down through the render props to other components.

WDYT? Is this completely against the nature of how React is expected to work? I understand the pattern is that components can control the props / state / UI updates they make from the data coming INTO them, but I understand that it could be an anti-pattern to stop the components from passing data down to their children. Understandably you could control this via using setState and only passing state down to the children, but sometimes you want to both use the internal state to manage the logic of the component and also have other logic controlling prop updates to children.

The use cases for this would be mainly:

  • Data Containers
  • API requests
  • Conditional API requests
  • Combining multiple containers for code isolation into a presentational component

I understand that each of these bullet points can be solved using various different means, but the new hooks API is so well thought out, understandable, and provides a clear pattern for controlling data INTO components, it would be beneficial to have a similar pattern on data leaving components (or context providers etc).

Thanks

Original post was put in facebook/react here.

[Hooks] Passing the Context only to useContext

One question that came up in DefinitelyTyped repo.

const { Consumer } = React.createContext(50);

function Example() {
  const context = useContext(Consumer); // Passing Consumer only
  return <p>Value: {context}</p>;
}

This will cause the warning

Calling useContext(Context.Consumer) is not supported, may cause bugs, and will be removed in a future major release. Did you mean to call useContext(Context) instead?

Why is that? I mean if this works now, why to remove it later? If the full context object will be required it kinda makes 3rd party libraries more confusing as they usually want to wrap the Provider. Is it expected that library authors will export custom hook? Feels like a strange limitation.

findDOMNode for function components

A ReactDOM.findDOMNode equivalent API for function components.

Any container component that doesn't render it's own view would benefit from this addition, for example:

function Foo (props) {
	useLayout(() => React.Children.node(node => element.focus()))

	return props.children
}

[Hooks] Hooks inside if statement

There may be edge cases where you'd like to put hooks inside a if statement.

I think hooks are great for lifting state up the component tree. This would work great together with components that either are controlled or uncontrolled.

You would split the component into three parts:

1. The Component
This is the actual component. It is totally stateless. All values and event handlers get passed in by the props. This part of the component is usually not exported.

function FormInputComponent(props) {
  const { label, value, onChange, className } = props;
  return (
    <div className={"input-field " + className}>
      {label}
      <input id={"asd"} value={value} onChange={onChange} />
    </div>
  );
}

2. The hook
This is the hook that is needed to create the state of the component. The property populated is later used to make the difference between controlled and uncontrolled.

export function useFormInput(initialState) {
  const { value: initialValue } = initialState;
  const [value, setValue] = useState(initialValue);
  return {
    value,
    onChange: e => setValue(e.target.value),
    populated: true
  };
}

3. The Stateful Component
This component stores the state in case the component is uncontrolled. If the component is controlled, it just passed down the props

export function FormInput(props) {
  let componentProps = useFormInput(props);
  if (props.populated) {
    componentProps = props;
  }
  return <FormInputComponent {...componentProps } />;
}

In this implementation you initialize the state twice. Which is unnecessary. It would be handy to just do this:

export function FormInput(props) {
  let componentProps = props;
  if (props.populated) {
    componentProps = useFormInput(props);
  }
  return <FormInputComponent {...componentProps } />;
}

Why should you care?

Just look at how easy it is to lift the state up to where you need it.

// uncontrolled

<FormInput value={"Harry"} />

// controlled 

const controlledInput = useFormInput({ value: "Harry" });
...
<FormInput label={"Name"} {...controlledInput} />

You can bubble the state up as far as you like.
The UserForm just uses to FromInputs.

// uncontrolled

<UserForm surname="Harry" name="Potter" />

// controlled 

const userForm = useUserForm({ surname: "Potter", name: "Harry" });
...
 <UserForm {...userForm} />

here are some examples:

https://codesandbox.io/embed/0xv205y2yw?autoresize=1&hidenavigation=1&view=preview

useEffect goes in inifinite loop with React Hooks

React/Redux application goes into an infinite loop on using useEffect with object references..

I am trying render pending todos for my application using useEffect.. and passing the array of todos as the second param in useEffect ..but why is not checking the values of the object ?

Container:

const mapDispatchToProps = dispatch => ({ actions: bindActionCreators(RootActions, dispatch) });
const Home = (props) => {
  const { root, actions } = props;

  useEffect(() => {
    getTodos(actions.loadPendingTodo);
  }, [root.data]);

  return (
    <Segment>
      <Error {...root } />
      <TodoList { ...root } actions={actions} />
  </Segment>
  );
};

export default connect(mapStateToProps, mapDispatchToProps)(Home);

Action:

export const loadPendingTodo = () => ({
  type: LOAD_PENDING_TODO,
  data: todoService.loadPendingTodo(),
});

Reducer:

const initialState = {
  initial: true,
  data: [{
    id: 0,
    text: 'temp todo',
    dueDate: new Date(),
    completedDate: '',
    isDeleted: false,
    isCompleted: false,
  }],
  error: false,
  isLoading: false,
  isEdit: false,
};

export default function root(state = initialState, action) {
  switch (action.type) {
    case LOAD_PENDING_TODO:
      return {
        ...state,
        data: [...action.data],
      };
...

    default:
      return state;
  }
}

getTodos Method:

export const getTodos = (loadTodo) => {
  try {
    loadTodo();
  } catch (error) {
    console.log(error); // eslint-disable-line
  }
};

Service:

export default class TodoAppService {
  loadPendingTodo() {
    return store.get('todoApp').data.filter(todo => !todo.isCompleted && !todo.isDeleted);
  }

Can anyone please help me out how to resolve this issue.. and there is no official documentation for this case too :/

Moreover changing the useEffect to the following works but i want to render on every change

  useEffect(() => {
    getTodos(actions.loadPendingTodo);
  }, []);

[Hooks] An official global state-management solution (a la `redux`)

Related to #68
I'm unsure if this is the appropriate place for this. If not, feel free to close.

The useState hook feels very similar to the way atoms work in reagent.cljs, except for one important part: in reagent, multiple components can share one atom so that all of them re-render when it changes, whereas useState only creates state local to the component that called it and only that component (and its children) re-render when that state changes. To say it another way, React hooks offer little to no affordance for doing scalable global state management in the fashion of redux or mobx, whereas reagent atoms do. However, I think it would be relatively simple to implement atoms for React with a light abstraction on top of useState and useMutationEffect.

I've already implemented a minimal version of Atom for react, and so far I really like the result. I'm curious to know what others think.

I'm just calling it react-atom for now and have published it (with TypeScript typings) to npm. Here are the docs. And here is a JS CodeSandbox and TS CodeSandbox to try it out live. If you're wondering, the API for it is mostly just plagiarized from clojure/script atoms.

I think there would be a lot of benefit to react.js providing something like this as an official way for doing global state management. Doing so would really help unify the community and library ecosystem, and it would allow some performance optimizations to be managed internally by react. It could be made more plug-able too, so as to enable the various niceties made possible by redux middleware.

The new Hooks proposal seems like the perfect opportunity to introduce something like this. What do you think?

How does this method even work outside of render? (eg. constructor, lifecycle methods, etc)

Since the pattern is that you have to have this consumer component, how do you get to the context when you're in the constructor exactly?

I'll give two examples of where you would want this.

Example 1: You want a simple state application

Given you have something like a Baobab tree, you want to make it available to your subtree so that components in the subtree can just use it (I gave state here as an example but it applies to anything really). Assuming components in the subtree have to "do stuff" with it how exactly do you do it?

The only way I see it with this new system is to start create THREE components every time you need to consume it: once to convert it to a higher order component, again to use the higher order componet to do the data manipulation and lastly you'd have your ui component.

The key point is that you want your subtree components to NOT GIVE A DAMN on what application state they're subscribed to. So if you say have a user component and several applications on the page, each with said user component, you want each component to work with the state passed down from the initialization code, not some shared mucked up state.

Example 2: You need to call up an arbitrary function.

So let's say I have some dispatch system, that given a "url" decides how to change the application, location history and so on.

And then I have various situations and components where I want to call this dispatcher. These may be simple or may be complex routines that require redirection to happen. Ideally what we want is for the top-most level to configure one of these dispatchers and all bottom levels to use the dispatcher they're given.

For simple cases such as a smart Anchor component that either invokes the dispatcher or acts as a regular anchor element, we can kind of suffer the silliness of having to wrap it all in a provider then bind it as an extra parameter in there. But once you start having the requirement for the dispatcher be somewhere that's a few calls down it just becomes a pain to deal with. Since now instead of simply being able to get to it easily whenever it needs to be passed to a function outside the component, every single call up to it has to have it binded over and over; likely just in case "eventually we might need it so god fodbid we have to change all the damn interfaces for every function"


Simple solution to the problem: the "consumer" needs to be easily usable in the constructor

Also, I might add, all of THIS is NOT a problem with the current system.

useResource

Assuming createResource is implemented to work akin to createContext, we could have a useResource API like useState to register a resource dependency. The resource will return a tuple โ€“ the data and a dispatcher to update the data.

Taking facebook/react#14248 as an example, the following model might look something like:

const TodoResource = createResource(data => fetch(`/details`))

function LeftPanel (props) {
  const [state, dispatch] = useState(props)
  const [resource, fetch] = useResource(TodoResource)

  return <button onClick={e => fetch(state)}>Click Me</h1>
}

function RightPanel (props) {
  const [resource, fetch] = useResource(TodoResource)

  return <h1>{JSON.stringify(resource)}</h1>
}

function App () {
  return (
    <Suspense fallback="Loading...">
      <LeftPanel/>
      <RightPanel/>
    </Suspense>
  )
}

The best practice to avoid unnecessary re-render and batch rendering in React Hooks

Two issues for this code

const [index, setIndex] = useState(0),
      [at, setAt] = useState(0);

handleState = () => {
  const nextIndex = evaluateIndex(window.innerWidth)
  if (nextIndex !== index) { <<<<<<<<<<<<< (1)
    setIndex(nextIndex);
  }

  const nextAt = evaluateAt(window.pageYOffset)
  if (nextAt !== at) { <<<<<<<<<<<<< (2)
    setAt(nextAt);
  }
}

useEffect(() => {
  window.addEventListener("resize", handleState);
  window.addEventListener("scroll", handleState);
  return () => {
    window.removeEventListener("resize", handleState);
    window.removeEventListener("scroll", handleState);
  }
});

unnecessary re-rendering issue

The if statment (1) and (2) are the way that I use to avoid the unnecessary render. Yes, two state variables are fine for now. If I have 10 state variables, then I have to put 10 if statements to perform a pre-check before calling setX. If so, it will be odd. Or did I do something wrong? And what is the best practice?

batch rendering issue

every time, I call setX, it will trigger the rendering immediately. for example, both nextIndex and nextAt are different from previous. The first setIndex(nextIndex) will trigger rendering along with the existing old at value because evaluateAt has not been executed. After the rendering is completed, it will continue to run evaluateAt and then setAt. At this moment, it will trigger rendering again with nextIndex and nextAt. The first rendering along with the existing old is not necessary. What should I do to avoid the first rendering with the old at? I want to batch them all. Should I change to use useState({at: 0, index: 0})?

How do this work when you need abstraction?

Scenario: You have Component X. Component X is a generic component in a "library" (assume this is a 1st-party shared-code style library, not 3rd party; ie. some git submodule, etc). Said component wants to be provided a certain well defined per-project entity "ThingA".

How does this Consumer/Provider relationship work when you want each project to be able to provide their own ThingA that guarantees the interface required by Component X but is not limited to it?

How do you make it so Component X has access to it's required Consumer, but said Consumer is required to be inside the library, and each project is free to create their own Provider that satisfies the Consumer for X, but said Providers are required to be outside the library.

Extra Optional Assumption: Assume that primarily the responsibility of Component X when using the context provided is to bridge functionality of the project, so the surface interface is consistent but the relationship is primarily in the sense of project passing project specific logic down for it to be called back (by Component X) to it in some other part of the project specific code.

[Proposal] Memo in another manner

Problem

memo approach is enough understandable. However is not 100% elegant solution... Especially if you have more "wrappers" like connect, withRouter, or other HOC:

export default memo(
  connect(mapStateToProps, mapDispatchToProps)(MyComponent),
)

Proposal

I will propose to do it following components configurations as displayName, propTypes, defaultProps...

MyComponent.displayName = 'NameOfMyComponent';

Something like:

export default function MyComponent() {
/* ... */
}


MyComponent.memo  = true;

or

export default function MyComponent() {
/* ... */
}

MyComponent.memo  = (nextProps, prevProps) => { /* ... */ };

Portals(Regions) in virtual dom

I like the idea of Portals and have a proposal to extend it

The idea, in short, is to have a special element in React to allow connect Portal to that element without real id in DOM element, that we have now. Something like:

const app = () => (
  <>
    <SomeApp />
    <React.Region regionId='GUID' />
  </>
)

const SomeWidget = () => {
...
}

ReactDOM.createRegion(SomeWidget, 'GUID')

So the idea is that we have Portal(region) anywhere in virtual DOM, that will be rendered asynchronously
when it is needed.

Currently, I am working on a project that uses a custom-made library (RxJs based Frint-js) to make something like this works.
So we have <Region name="some region" />. Our libraries look for Region and connect our widgets to react application dynamically with all props that are needed

Suggestion for using forwardRef for imperative API (QUESTION)

I'm not sure it's the right place but it's not a bug so I'm not sure where is the right place. (Maybe a new API)

In project I work on, there is a TextField component which is more than a wrapper for <input>. It wraps the <input> element with a <div> to overcome some limitation that needed by a requested design.

TL;DR

The forwardRef API as is seems to not be usable for this use case, because:

  1. The actual wrapper is a <div> which is not what you expect when you manually want the textfield element. Because you would expect the <input> for using commands such as #focus() or #checkValidity() and so

  2. But sometimes you do need the <div> container because you need to measure the element which the inner <input> is useless for. ReactDOM.findDOMNode from what I've search isn't recommended. But it also blocks the ability to use the code in React Native or other renderers

Today, I add functions to the component class such as TextField#focus() (that calls focus on the <input>) and I'll probably need to add one for TextField#getClientRects() (for measure the <div>). But, it seams redundant and not very stable as more native DOM functionality is needed to warped by the component class for imperative needs use cases (as the project grows and more needs are added).

What do you think I should do?
Maybe forwardRef need some tweaks?
Maybe a new API is needed?
Or maybe the current scenario of what I do is the least worst option?

Static Lifecycle Methodsโ€™ getDerivedStateFromProps access to previous props

While reading 0006-static-lifecycle-methods.md, I noticed this note in the detailed design for static getDerivedStateFromProps:

Note that React may call this method even if the props have not changed. If calculating derived data is expensive, compare next and previous props to conditionally handle changes.

@bvaughn With getDerivedStateFromProps being a static method and not receiving prevProps as an argument, how would this be accomplished?

The example illustrating use of this pattern notes that โ€œif the calculation is fast enough it could just be done in renderโ€, so Iโ€™m guessing that the primary motivation for using getDerivedStateFromProps should be more expensive calculations. But I also saw the discussion about the static deriveStateFromProps(props, state, prevProps) { proposal, which suggests that this kind of a check wonโ€™t be possible.

An aside: as a React consumer, I would expect static get** component methods to be there as something I would invoke for use in my component, rather than something I would provide for the libraryโ€™s use. Though deriveStateFromProps would still require a doc lookup for most developers, it does look less like something I would confuse with my own component properties or methods.

Iโ€™m posting this as an issue because I cannot comment in the original PR and because it seems there is an issue that needs to be resolved one way or another with the text of the RFC. I hope this is the right way to go about this!

[Hooks] Alternative useCallback implementation

According to this page useCallback(fn, inputs) is equivalent to useMemo(() => fn, inputs)

I suggest another one (implemented with current hooks):

function useHandler(handler) {
  const holder = useRef();
  holder.current = handler;

  // TODO: avoid useMemo?
  return useMemo(
    () =>
      function(...args) {
        return holder.current.apply(this, args);
      },
    []
  );
}

It has advantages:

  • no need to specify inputs, latest instance of handler will be called
  • result of useHandler is constant for all rerenders

Effects in object definition

Effects have to be specified in a specific order, but object keys in JavaScript are not guaranteed to be sorted in order they are defined.

Can I use useCallback like this?

const actions = {
  increment: useCallback( /* ... */ ),
  decrement: useCallback( /* ... */ ),
};

Or should I use it like this?

const increment = useCallback( /* ... */ );
const decrement = useCallback( /* ... */ );
const actions = {increment, decrement};

[Hooks] `useProvider` for setting context.

For providing context we need to create an element.

<SomeContext.Provider>{props.children}</SomeContext.Provider>

It will be good we have a new primitive hook provide context for underlying consumers.

suggested name: useProvider

Example 1: Providing state

old version

const ThemeContext = React.createContext("green");

function useTheme() {
  return useContext(ThemeContext);
}

function App() {
  const [theme, setTheme] = useState("blue");
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <div>
        <Child />
      </div>
    </ThemeContext.Provider>
  );
}

function Child() {
  const { theme } = useTheme();
  return <Item color={theme} />;
}

new Version

const ThemeContext = React.createContext("green");

function useProvideTheme(initialValue) {
  const [theme, setTheme] = useState(initialValue);
  useProvide(ThemeContext, { theme, setTheme });
}

function useTheme() {
  return useContext(ThemeContext);
}

function App() {
  useProvideTheme("blue");
  return (
    <div>
      <Child />
    </div>
  );
}

function Child() {
  const { theme } = useTheme();
  return <Item color={theme} />;
}

Example 2: Stack

const StackContext = React.createContext([]);

function useNewLayer(name) {
  const oldValue = useContext(StackContext);
  const newValue = [...oldValue, name];
  useProvider(StackContext, newValue);
  return newValue;
}

function useStack(name) {
  return useContext(StackContext)
}

[Hooks] useEffect hook api advice

copied from: #68 (comment)

hope for api like this

current api:

useEffect(
  () => {
    const subscription = props.source.subscribe();
    return () => {
      subscription.unsubscribe();
    };
  },
  [props.source],
);

I think better api is:

useEffect(
  function didPatchDOM(prevProps){
    const subscription = props.source.subscribe();
    return function willPatchDOM(nextProps) {
      subscription.unsubscribe();
      return false; // false means do not run didPatchDOM, and use this old willPatchDOM to handle the nextNextProps.
    };
  }
);

// then
// componentDidMount
useEffect(function componentDidMount(prevProps) {
  assertEqual(prevProps, null);
  do_effects();
  return nextProps => false;
});

// componentWillUnmount
useEffect(function componentWillUnmount(prevProps) {
  return nextProps => {
    if (nextProps === null) {
      do_effects();
    }
  };
});

// the real subscription example
useEffect(function realSubscription(prevProps) {
  if (prevProps === null) {
    const subscription = props.source.subscribe();
    return nextProps => {
      if (nextProps === null) {
        subscription.unsubscribe();
      }
      return false;
    }
  }
  return function willNeverReach() { return false; }
})
  1. those functions name: didPatchDOM and willPatchDOM are just for documenting, not show the lifecycle;
  2. false means do not run didPatchDOM, and use this old willPatchDOM to handle the nextNextProps: so, prevProps and props in closure willPatchDOM may be objects of very early time, they are ancient;
  3. I don't know if it's a good idea: if willPatchDOM has no return, it returns true.

copied from: #68 (comment)

the changes between my thought api and current api are:

// current api:
useEffect(
  () => {
    const subscription = props.source.subscribe();
    return () => {
      subscription.unsubscribe();
    };
  },
  [props.source],
);

// my thought api:
useEffect(
  // 1. here is a param: prevProps
  (prevProps) => {
    const subscription = props.source.subscribe();
    // 2. here is a param: nextProps
    return (nextProps) => {
      subscription.unsubscribe();
      // 3. this returned function should return a boolean
      return false;
    };
  }
);
  1. prevProps: it should leave the comparing logic to us rather than the second param of useEffect even though the second param of useEffect is calculated by us in the SFC. If the element is just constructed, prevProps is null;
  2. nextProps: we need param nextProps to decide what should we do. If the element will unmount, nextProps is null;
  3. return a boolean: this boolean indicates should react run the effect function after it have patched the real DOM.
  4. the nextProps function, well, the function receives the nextProps: this function closure wraps many outside variables like props, prevProps. If it returns false, then it will not be updated, and in next render term, react will still run it, then things may be: prevProps -> props -> nextOneProps -> nextTwoProps -> ... -> nextNProps, and thenextProps is infact the nextNProps.

copied from: #68 (comment)

Oh, the function closure which receives the nextProps wraps so many outside variables like props, prevProps, it infact wrap state, setState too.

And since it may not be updated in one render term, the nextProps param is infact nextNProps, we have no means to get the next(N-1)State.

So, another advice:

let [state, setState, getState] = useState(xxx);

getState won't change, and it will always return the latest state.

Skip second argument in React.createElement()

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

What is the current behavior?
React.createElement(component, props, children), when props aren't needed, you still have to pass null or {} as a second argument.

What is the proposal?
Being allowed to omit the second argument, allowing this:

import React from 'react'
import Foo from './Foo'
import Bar from './Bar'

const o = React.createElement

const App = () =>
  o('div',
    o('h1', 'Hello'),
    o(Foo),
    'Ok!',
    o('p', 'How are you?'),
    o(Foo,
      o(Bar, { key: 'hi' },
        o('span', 'Heya!')
      )
    )
  )

Demo code?
I currently, very naively, use this:

function o () {
  var argumentsArray = Array.from(arguments)
  var x = argumentsArray[1]
  if (typeof x === 'string' || typeof x === 'number' || Array.isArray(x) || (typeof x === 'object' && Object.keys(x)[0] === '$$typeof')) {
    argumentsArray.splice(1, 0, {})
  }
  return React.createElement.apply(this, argumentsArray)
}

New React 16 Context - Nesting and overrides

I use the old context in my metadata driven application. The metadata determines the structure of the component hierarchy. A common usage is to provide a child component information from a container that may be n levels above the child. It's possible that the child is not contained within a container. Also it is possible there are multiple levels of nested containers.

One example of nested context calls and overrides is when a unique identifier needs to be generated based on the nesting. The pattern I use is that the child generates its own uniqueId by calling a function in context to get the unique id of its parent and incorporating that into its own id. The immediate parent container provides this function, in order to ensure uniqueness the container looks for and calls this function from it's own context in order to incorporate its own parent container's uniqueId in its id.

Perhaps I'm missing something but, with the proposed new API I cannot see how the child can dynamically obtain a reference to the context it's parent provides. How can this link be made if we do not know until run-time which context provider a child should consume?

[hooks] pass inputs as arguments of factory in `useMemo`

A little apis changes of useMemo.

function useMemo<T, I>(factory: (...inputs: I) => T, ...inputs: I): T;

benefit of the changes:

  • we could declare the factory as pure function.
  • only pass inputs once
  • type checks like flow or typescript friendly.
    • arguments length and type check

Example:

function computeExpensiveValue(a: string, b: number): any;

const memoizedValue = useMemo(computeExpensiveValue, a, b);
// here, inputs (a, b) is not only for triggering memoizedValue to refresh, 
// but also for type checking. 

A separate version of React without support for classes

The objective with this is that there are two versions of React.

  • With support for both functional components and class components, as it currently remains.
  • With unique support for functional components.

This will allow developers to continue working on their legacy projects by implementing the full version of React.

While in the new projects they may choose to use only class components or functional components.

This last would bring a considerable reduction of the bundle size.

[Hooks] useContext tuple

The single return value from useContext leaves much to be desired. Is there any reason that useContext does not return a value/setter tuple in the fashion of useState?

This would allow React to provide a guarantee of more granular updates: That is instead of the need to re-render a provider high up in the tree. The consumer component could update the context with only consumer dependencies subscribing to the update.

import {useContext, createContext} from 'React'
import {render} from 'ReactDOM'

const themeContext = createContext('red')

function A (props) {
	const [theme, setTheme] = useContext(themeContext)
	return <button onClick={() => setTheme('blue')}>{theme}</button>
}

function B (props) {
	const [theme, setTheme] = useContext(themeContext)
	return <button onClick={() => setTheme('orange')}>{theme}</button>
}

ReactDOM.render(<div>
	<Fragment><A></A></Fragment>
	<Fragment><Fragment><B></B></Fragment></Fragment>
<div>, container)

An example akin to a router could use this to guarantee granular updates to consumers that does not in effect rely on traversing all components within a provider to reach all consumers.

const routerContext = createContext('/')

export function useRouter () {
	const [route, setRoute] = useContext(routerContext)
	
	useEffect(() => {
		window.addEventListener('popstate', () => {
			setRoute(location)
		})
	}, [])

	return route
}

export function Link () {
	return <a onClick={() => window.dispatchEvent(new Event('popstate'))}></a>
}

function routerConsumer ({href, children}) {
	const route = useRouter()
	return route === href ? children : null
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.