diff --git a/text/0000-signals-and-slots.md b/text/0000-signals-and-slots.md new file mode 100644 index 00000000..c518906c --- /dev/null +++ b/text/0000-signals-and-slots.md @@ -0,0 +1,210 @@ +- Start Date: (2019-12-04) +- RFC PR: (leave this empty) +- React Issue: (leave this empty) + +# Summary + +This proposal introduces the concept of signals and slots to React. + +Signals and slots are used in the Qt C++ framework to implement the observer +pattern without needing any boilerplate code. It enables components to be +completely decoupled from the internal state or interface of other components. +Adding this to React will also decouple components from their parent component +while also isolating the internal state of each component. + +# Basic example + +```js +import React, { useSignal, useSlot, useState } from 'react'; +import PropTypes from 'prop-types'; + +function TextInput () { + const [ value, setValue ] = useState(''); + const emit = useSignal('updated'); + const onChange = (e) => { + setValue(e.target.value); + emit(e.target.value); + }; + return +} + +TextInput.signals = { + updated: PropTypes.string +}; + +function Banner({ name }) { + return

Hello, {name}!

; +} + +Banner.propTypes = { + name: PropTypes.string +}; + +function App () { + const name = useSlot('updated'); + + return ( + <> + + + + ); +} +``` + +# Motivation + +Modifying the state of another component in React inherently leads to tightly +coupled components. The most widespread practice today is to use `useState()` or +`useReduce()` and then pass both the values and the callbacks to modify those +values to a child component. + +That exposes the internal state of each component, and it leads to all +components being dependent on each other's interface. Change even one parameter +name, and the whole thing can fall apart. + +Signals and slots can solve the problem of tightly coupled components in a +natural, React-like way. + +Because the signals and slots API is implemented using React Hooks, it is +completely optional and backward-compatible. + +# Detailed design + +The signals and slots concept in Qt has been one of the most successful in the +history of software engineering. It's typesafe, decoupled, extensible, and +*fun*! It enables components to communicate with their parents and siblings +without being tightly coupled to each other. + +In Qt, slots are methods defined in a C++ class, and signals are functions +defined in a class but are implemented by a preprocessor, not the developer. +The React implementation differs from the Qt framework in that it connects +children to parents instead of child-to-child. + +This design decision was made because, unlike C++, React does not provide +an easy way of keeping track of an individual instance of a child component. +Fortunately, React does provide an easy way of providing the exact same thing: +JSX props. + +A parent can connect to a signal and then pass the value as a prop to any +child component. That enables defacto child-to-child communication without +any extra boilerplate code needed. + +In this proposal: +*Signals* are events that are defined by React components using the `useSignal` +hook. +Slots are values assigned by listening for a signal using the `useSlot` hook. + +Above all, I wish to stress: **Signals and Slots is not for state management. +It is for inter-component communication.** + +# Drawbacks + +Most of the drawbacks of React Hooks also apply here. +A non-exhaustive list of drawbacks specific to this proposal include: + +- May require considerable changes to the internals of React. +- Might be difficult to implement a proof-of-concept outside of React. +- If documentation is unclear, it could lead to confusion about how this + is used and what to use it for. +- Additional prop-types are needed for signals in order to guarantee type safety. +- The current design uses strings as params for `useSlot()`, which makes + type-checking with TypeScript/Flow much more difficult. +- The same end result (albeit at a cost) could be achieved with existing + userspace state-management libraries (Redux) or global event emitters. +- Signals and slots is not suitable for sending large amounts of data or a + large number of items. +- If multiple children emit the same signal name, the parent component has no + way of knowing which child emitted it. + +# Alternatives + +A number of alternate syntaxes were considered. + +The most recent proposal used child-to-child connections but required the use +of refs to keep track of each individual child. That was discarded for being +too complex and difficult to understand. + +One alternative considered was: +```js +const [ signal, slot ] = connect('clicked', 'message') +