Comments (10)
@srcspider New context api is not singleton. Data you pass is kept in the tree. You may add many sibling providers and every consumer will consume data of only own provider.
const Cx = createContext(0);
const Component = () => (
<>
<Cx.Consumer>
{value => value /* 0 */}
</Cx.Consumer>
<Cx.Provider value={1}>
<Cx.Consumer>
{value => value /* 1 */}
</Cx.Consumer>
</Cx.Provider>
<Cx.Provider value={2}>
<Cx.Consumer>
{value => value /* 2 */}
</Cx.Consumer>
</Cx.Provider>
</>
)
from rfcs.
Every time your Chaos
component is rendered, it will produce different outputs. This is not how React components are supposed to work.
That being said, here's how you could create the same "game" you've shown using the new context API: https://codesandbox.io/s/24y59mj0jn
The solution you provided, to my understanding has the same limitations as if I were to just have a random nodejs module exporting ThingA and every other module importing said module (you just did it with a lot more code). I would achieve the same result and suffer from the same limitation, that being that it is inherently a singleton context. I never want singleton contexts. Even if it is a single ThingA in the application, having it as a singleton means, "there can never be anything ThingA on the page."
No, although I guess I can see how you came to that conclusion. Maybe this example helps clarify?
import {ComponentX, ThingAProvider} from 'some-component-x-library';
<>
<ThingAProvider value="foo">
<ComponentX /> <!-- gets "foo" as "thing a" -->
<ThingAProvider value="bar">
<ComponentX /> <!-- gets "bar" as "thing a" -->
</ThingAProvider/>
</ThingAProvider>
<ThingAProvider value="baz">
<ComponentX /> <!-- gets "baz" as "thing a" -->
</ThingAProvider>
<ComponentX /> <!-- gets null as "thing a" -->
</>
The context API supports overriding values via nested providers.
from rfcs.
What's this comment in relation to? I don't think you meant to make an issue here? (Same as the other issue you made)
from rfcs.
@srcspider are you referring to context?
from rfcs.
@amrdraz yes, see discussion in #23
from rfcs.
I'm a little unclear on what you're asking here too. Terms like "component x" and "thing a" are a little too ambiguous. But I think you're asking how you could write a library with a component that consumes a context value that must be provided by users of your library. In that case, you could do something like this:
Your Component X library
const ThingAContext = React.createContext(null);
// Export the provider for libraries using Component X.
// These libraries are required to supply their own context value.
export const ThingAProvider = ThingAContext.Provider;
// Export Component X for use however it is intended.
export const ComponentX = props => (
<ThingAContext.Consumer>
{thingA => {
if (thingA === null) {
throw Error('You must provide a value for Thing A');
}
return (
<SomeInternalComponentThatRequiresThingA
{...props}
thingA={thingA}
/>
);
}}
</ThingAContext.Consumer>
);
Code that uses your library
import {ComponentX, ThingAProvider} from 'some-component-x-library';
// I need to specify my own value
<ThingAProvider value="My own value for thing A">
<div>
<div>
<ComponentX foo={123} bar="abc" />
</div>
</div>
</ThingAProvider>
from rfcs.
This is clearly getting us nowhere fast. I've given an answer to your post at the end, but the short of it is that it's not what I meant.
To make this "problem" as simple as possible I've boiled it down to a "fizzbuzz"-like problem instead. This way it's very simple and we just have to talk code. Solve the problem within the given limitations and any confusion goes away.
Template
https://jsfiddle.net/54s6p6ux/
class Player extends React.Component {
render() {
let chosenColor = undefined;
return (
<div>Player chose color {chosenColor}</div>
);
}
}
// This simulates there being a boundry betweent the Player component
// and the game component. If there was none the obviously there is no
// need for a "context" system of any sorts at all!
function Chaos(/* no arguments allowed! */) {
let comp = <Player/>;
let randomTimes = Math.floor((Math.random() * 10) + 1);
while ( --randomTimes > 0) {
comp = <div>{comp}</div>
}
return comp;
}
class Game extends React.Component {
render() {
return (
<React.Fragment>
<h2>{this.props.name}</h2>
<p><b>Game Colors</b>: {this.props.colors.join(', ')}</p>
<Chaos/><Chaos/><Chaos/>
</React.Fragment>
);
}
}
for (let i = 1; i <= 100; ++i) {
let colors = [];
for (let j = 0; j < 10; ++j) {
colors.push(`c${i}-${j}`);
}
let container = document.createElement('div')
document.body.appendChild(container);
ReactDOM.render(
<Game name={`Game ${i}`} colors={colors} />,
container
);
}
Limitations
- you may not change the
Chaos
function at all! - only the
Chaos
function is allowed to work with Player (simulates realworld deep hierarchies) - the colors must pass though (no cheating using time based race conditions or other methods)
Objective
100 games are created on the page. Each game has 9 unique colors (names not important). Each game has 3 players. Each player randomly chooses one of the 9 colors.
A template with all the boring stuff is provided. You just have to fill in the code so that colors make it to the players and they print one at random.
Example
Here is the solution using the classic context system.
https://jsfiddle.net/8xqwfma1/
With regard to your example, just so it doesn't go unanswered:
The solution you provided, to my understanding has the same limitations as if I were to just have a random nodejs module exporting ThingA and every other module importing said module (you just did it with a lot more code). I would achieve the same result and suffer from the same limitation, that being that it is inherently a singleton context. I never want singleton contexts. Even if it is a single ThingA in the application, having it as a singleton means, "there can never be anything ThingA on the page."
from rfcs.
That being said, here's how you could create the same "game" you've shown using the new context API: https://codesandbox.io/s/24y59mj0jn
The solution is wrong because you manipulated the Chaos function (see Limitations section in initial post). Chaos represents parts of the code you don't manipulate (such as 3rd-party intermediate components that sit between your root "Game" component and your "Player" component, in this case).
Based on what TrySound posted, the "solution" is this https://codesandbox.io/s/98jrv09qvw
@srcspider New context api is not singleton. Data you pass is kept in the tree. You may add many sibling providers and every consumer will consume data of only own provider.
Well I'm happy to be proven wrong. And you're right I was thiking that due to how simplistic it looks (all blog posts about it so far don't touch on it either I think).
But at the same time I don't feel too good by how complicated it seems to actually be internally. The fact it can't be used in the constructor or any function outside of render is a big led down still. I'm to assume that the reason it's not able to pass it directly to the constructor or some componentDidRecievedContext
is not just some style choice? and has something to do with limitations behind the logic of workLoop
, beginWork
and updateContextConsumer
in React's internals?
Seems I'm doomed to have to create 3 separate components each time I need to create one.
But again thanks for clearing up it's not a singleton pattern.
from rfcs.
Closing based on clarification from @gaearon in #23
from rfcs.
The solution is wrong because you manipulated the Chaos function (see Limitations section in initial post).
Right. I arbitrarily placed the Context.Consumer
component within the Chaos
component because (in the isolated example) it seemed to make sense. You can move the consumer into Player
if you prefer, or anywhere between the Context.Provider
and Player
.
Updated example at:
https://codesandbox.io/s/j2ny0xnw23
The fact it can't be used in the constructor or any function outside of render is a big led down still.
For anyone else who sees this thread in the future, this comment on issue #23 explains how you can access context values in the constructor (or elsewhere outside of render
). You just need to lift the consumer up a level.
For example, "lifting up" can be done with a higher order function, like so:
function withColorsContext(Component) {
return function ColorsContextConsumer(props) {
return (
<ColorsContext.Consumer>
{colors => <Component {...props} colors={colors} />}
</ColorsContext.Consumer>
);
};
}
from rfcs.
Related Issues (20)
- onScroll event propagation HOT 5
- [Feature Request]: distinguish "what" and "when" dependencies in useEffect HOT 2
- [Feature Request] Return ref rather than forwardRef HOT 1
- the ability to check if something is function or class or an arrow function HOT 5
- npx-create-react-app creating a folder tempnodejsnpm HOT 2
- [Feature Request] Can we do some Static Analysis for diff with babel ? HOT 6
- Improving the RFC workflow process HOT 18
- useIf: Conditional hooks HOT 6
- Is useReducer an overengineering? HOT 10
- Improve profiling react applications HOT 1
- Introduce GUI tooling to speed up web application development HOT 1
- [React Server Components] Idea to simplify overall design HOT 11
- psql: could not connect to server: No such file or directory HOT 1
- [Question] The new JSX transform HOT 1
- [Feature Request]: Add array of updated deps indices to `useEffect` hooks arg HOT 1
- [Feature Request]: React Hooks `Equality` **AKA:** `[isEqual]` Callback HOT 3
- Functional Attribute/Prop Node HOT 2
- [Feature Request][eslint-plugin-react-hooks] no-ref-checks, display error when using useRef's return value as condition HOT 1
- [Feature Request]: useStateRef HOT 5
- Typo: 'exiting' might be 'existing' HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from rfcs.