I'm trying to build a reusable component that I can use to load ads via Doubleclick (DFP) asynchronously. The problem I'm running into is there are several dependencies before an ad can actually load, and I'm not sure the best way to handle the communication and sequence. Because the ad components render anywhere on the page and don't render as part of a parent, I'm not sure how to handle it since I can't embed ads inside of a parent to communicate.
In this article:
http://facebook.github.io/react/blog/2013/11/05/thinking-in-react.html#step-4-identify-where-your-state-should-live
There is this potential solution, but after some searching I wasn't able to find an example that doesn't tie the rendering together.
"If you can't find a component where it makes sense to own the state, create a new component simply for holding the state and add it somewhere in the hierarchy above the common owner component."
Here are the details/steps:
1). I first need to load the JS http://www.googletagservices.com/tag/js/gpt.js
This creates a global googletag object.
2). I need to set global targeting (i.e. site name, content type, etc) that will apply to all ad tags (configurable).
Example
googletag.pubads().setTargeting('site', window.location.hostname);
3). I need to define the ad slots and targeting for individual ad slots that React rendered.
googletag.defineSlot('/12345/zone', [[300, 250], [300, 600]], 'div-gpt-ad-300x250-0').addService(googletag.pubads()).setTargeting('pos', 'sidebar_300_1');
4). I need to specify settings globally
googletag.pubads().collapseEmptyDivs();
googletag.pubads().enableSingleRequest();
googletag.enableServices();
5). I need to render the ad slots (with the option to render individually or globally)
googletag.cmd.push(function() {
googletag.display(divIdAttribute);
});
or just
googletag.display();
I Initially created two components: a DFPManager that loads the required Javascript once, and then a DFPAd that actually handles the individual ads. I couldn't figure out the communication.
I also need to be able to reload individual ads based on external events (i.e. ajax page change) or create new ads as a user scrolls into infinite scroll content.
Is the best solution to manage my own events system where I put listeners in my components to depend on external events, or is there a better way to manage this? In that case I wouldn't necessarily need a DFPManager since no rendering takes place there and I could trigger an event when ready. This is a similar problem for analytics tracking where I need to track pageviews based on external events, but only after first loading an initial javascript (i.e. Google Analytics tracking js).
<div id="300-250-ad"></div>
<script type="text/jsx">
/** #jsx React.DOM */
React.renderComponent(
<DFPAd size={[[300, 250], [300, 600]]} targeting={[["pos", "sidebar_300_1"]]} />,
document.getElementById('300-250-ad')
);
</script>
I may be overthinking this. Appreciate any suggestions.
Turning DFPManager into just an object which fires change events sounds like a good plan. It wouldn't be a React component itself any more. The individual ad components could accept the store object as a prop, then subscribe to the change events in componentDidMount, and unsubscribe in componentWillUnmount. The onChange handler could call setState with the current data from the store, which will trigger a re-render.
The store could be implemented like the store in the Flux TodoMVC example, which uses an EventEmitter base class. If there is no interactivity, using a full dispatcher is probably overkill, since there won't be any user actions by the sounds of it.
Related
My app is structured as follows:
<App>
<SelectItems/>
<Tabs>
<Tab> <Window1> </Tab>
<Tab> <Window2> </Tab>
</Tabs>
</App>
The App has a state element called allItems, and SelectItems is used to narrow this down to App.state.selectedItems. The tabs work in a way that only one of the tabs shows at a given time, it's a component from React-Bootstrap.
The Window1 and Window2 display data that depend on the selectedItems, the data (let's call it data1 and data2) is obtained via a computationally intensive api call. Here's the problem. The cleanest way to do this while thinking in React is to "pull the state up", so that data1 and data2 are updated from App whenever the selectedItems change, and the data is then stored in the state of App and passed down to the tabs. This however is inefficient, because only one Window is showing at a given time, so I'm wasting time updating data that I'm never showing. In my actual app I've got many tabs, so it's a real problem.
What's a way of fixing this? I want the Windows to update whenever the App.state.selection changes, and I looked at static getDerivedStateFromProps(props, state) but it looks like (but I'm not sure) that this function won't work because the state updating would be asynchronous, as it requires an API call.
Are there general strategies one can use in this situation?
Thanks!
You have a couple of options, the first one is the one most people would recommend (because it is a popular option) and that is to use Redux to manage your application state.
Redux will allow you to keep all your data in a separate "store". Then from your components you can connect to the store and access the data which is relevant to them. Those components should only update when the data they are interested in is changed, any other changes to the store will be ignored.
There are a lot of good tutorials on using Redux and React together which you can find online - apparently the ones on egghead are pretty good, you can maybe try this one to get started.
Your other option might be to use a PureComponent so that you can limit when your component will re-render to only when it's props or state change rather than if the parent re-renders. You can read about this here. It's actually similar to the previous solution are the connect function provided by the react-redux library to connect to the Redux store wraps your component in a PureComponent.
I'm working in a project to to replace a large Silverlight application with a web application using react (and redux). There's multiple tabs and subtabs that the user can jump between.
We are using redux to save tab/subtab-state. The current solution uses cloneElement to inject previous state and a function (onSaveState) that all components needs to call in order to save state.
Is there a way to avoid having the save/load boilerplate code in all pages on the tabs+subtabs, for example by getting the state of a child component?
An ugly solution is to save the entire component (including the state) but this introduce a lot of overhead + having the react virtual DOM handle something that is not actually rendered may lead to several other problems.
Note: We are not allowed to change the overall UX.
Update: What we want to accomplish is to make save and load of state as simple as possible when the user jump between open tabs. A user can open several tabs of the same type. Most tabs have undo-functionality. It's a large application with 150+ views (not counting all smaller dialogs). To make the save/load of state from the redux store as seamless as possible it would have been nice to inspect the state(readonly) of a component (from a parent) just to save it. We have an ok solution today as I mentioned where we get the previousState as a prop and we call onSaveState on componentWillUnmount with the current state. I don't need an explanation of how redux works, I'm simply interested if it's possible to inspect the state of component from the outside (for example from a parent component) in a way that is not to hacky?
If you have complicated save/load code, don't put it all in the parent component and inject it into the children. Move it into shared files that the children can consume on their own.
Put loading code in a reducer file. Put saving code in an action file. Then let the tab components each require the new files so they can connect to the reducer and dispatch the action.
See Redux Basics for a thorough example.
Tools I'm Using: Reactjs 0.14.7, react-router 2.0.0 (Flux Pattern)
Note: I tagged Redux, just cause I got a hunch(I haven't used it) that what I'm experiencing might be one of the reasons people rave about it.
I understand that react-router already manages which parts of the
component tree are currently in view and renders the components based on the state of the current view tree.
Question:
But what if, based on the components in view, one component needs to know what other components are also in view and behave differently depending on what other components are in view(the view state)? What strategy would you suggest to allow components to be aware of the other components in view?
My Current Solution:
I currently am trying to use the URL to infer this global state, and even parsing it and putting it into a store in order for components to be aware of the view state by listening to changes from that store.
My Issue With This Solution:
In a nutshell managing that view state with a store becomes a highly entangled process with extra actions sprinkled all over the code.
1) Actions must be called for any user event that change the route.
2) Action need to be fired when navigating outside of components(I think its much cleaner to keep action firing in components(feel free to debate that one).
3) You must also consider the back button(currently using react-router onEnterHooks to catch when that happens).
Yet I really like the concept of encapsulating the view state because I can imagine that it creates a nice mental model and also smarter components, but just parsing the current URL and using a utility file to determine the current view state when needed, seems like a much easier/cleaner solution to manage then a store that contains the current view state.
Components should never need to know what other components are being rendered, that's one of the fundamental concepts of React. You're trying to extract the "view state" from your component tree, when your component tree should be determined by your state. If you're already using Flux, you need to keep that information in the store, where it will be made accessible to any component that subscribes.
Flux isn't about making development easier or faster for an individual, it's about enabling practices that make it easier to keep a mental model of what an application is doing. This might come at the expense of some simplicity.
Redux is a refinement of Flux that combines the multiple stores that can be subscribed to individually with a single large state tree, with different parts of the tree created by different "reducers" -- functions that take a state and an action and return a new state. It is exactly "a store that contains the current view state." What you describe is also a pretty good description of the type of development common in hacked together jQuery applications, the type of development React seeks to avoid.
I think the core of your misunderstanding falls into how React component's should be layered. It's a tricky topic, and re-aligning your thought process to accurately understand what is a state vs. prop in your model, is a unique challenge.
But the solution to this problem you are facing is simply to order your components more 'correctly'.
At a high level, each component should only care about the props that are passed to it, and not about anything else whatsoever. However, which props are passed are determined by it's parent Component. As a result, that parent can make those decisions, which then have an end result in the child.
As a simple but practical example;
var Parent = React.createClass({
funcA: function(){
this.setState({propB: 'something new!'});
},
render: function(){
return (
<div>
<ChildA propA={this.state.propA} funcA={this.funcA} />
<ChildB propB={this.state.propB} />
</div>
);
}
});
With this layout of concerns, ChildA is capable of handling user input, passing it to funcA which then impacts ChildB. But all of this happens without the Child components knowing anything about one another whatsoever.
How do you create a ReactJS component that reaches multiple levels up the component/DOM hierarchy?
A good example of this is a Modal. I want to trigger and control the modal from a child nested way down in my app, but a Modal requires that the DOM be much higher, most likely all the way up as a child of the document body.
I'm considering a "portal" pattern, as described here: https://github.com/ryanflorence/react-training/blob/gh-pages/lessons/05-wrapping-dom-libs.md#portals
FakeRainBrigand even wraps the pattern up nicely in a mixing in this post: https://stackoverflow.com/a/26789089/586181
This feels like a hack to me. Great if you want to use a non-react library like jquery-ui, but without that need breaking out of react just to render a react component somewhere else in the DOM seems like overkill. Is there a more "React" way of achieving this?
Thanks
I'll leave this best to the react documentation. If you must have buried React elements that need to communicate with other elements outside of their Parent Child or possibly even grandparent than see the below.
For communication between two components that don't have a
parent-child relationship, you can set up your own global event
system. Subscribe to events in componentDidMount(), unsubscribe in
componentWillUnmount(), and call setState() when you receive an event.
https://facebook.github.io/react/tips/communicate-between-components.html
I've written a library to help with this. I avoid the DOM insertion hacks used by Portal strategies out there and instead make use of context based registries to pass along components from a source to a target.
My implementation makes use of the standard React render cycles. The components that you teleport/inject/transport don't cause a double render cycle on the target - everything happens synchronously.
The API is also structured in a manner to discourage the use of magic strings in your code to define the source/target. Instead you are required to explicitly create and decorate components that will be used as the target (Injectable) and the source (Injector). As this sort of thing is generally considered quite magical I think explicit Component representation (requiring direct imports and usage) may help alleviate confusion on where a Component is being injected.
You can completely use my library to bind a ModalComponent to a root level component that you decorate with the Injectable helper. I plan on adding an example of this use case soon. It even supports natural props pass throughs, and all the various component types i.e. stateless/class.
See https://github.com/ctrlplusb/react-injectables for more info.
I'm trying to wrap my head around Facebook's Flux...
Say I have an app with a side menu that can be hidden and shown via a button in the header.
My header is one component, my side menu is another component.
Currently my Header component just sets a class on the HTML div side menu element which gets animated to hidden by CSS.
What's the general idea here?
ReactJs doesn't really care about how it gets its data (how it's data is getting passed in or how that data should be handled across the web application). That's where Flux comes in, it creates a functional approach on how data is handled. Data-flow is essentially:
Action -> Data Store -> Component
Mutation of data happen through calling Actions. The Data Stores themselves have to listen on the actions and mutate the data within the store. This keeps the data structure and logic flat.
In your case, your dataflow would probably look something like this:
Header --> User Click --> Fires action --> Updates store --> Side menu listening and responding to that store change.
Your case is a simple example which you probably don't really need Flux. I think it's easier if you have a parent component that maintains the view state logic, and use props/callbacks for the 2 children components (side menu and header). But a more advanced example that you need to make ajax calls and maintain session, Flux would become useful. Like if you have a Login Component, and you want to show different side-menu options and header options depending on user:
Login Component --> User Logins --> Calls Action #signIn --> Showing Loading State
--> Dispatcher handles action (make api call to authenticate user and load user data)
On success (for the api call), alert sessionStore, and populate store with data
On error, maybe fire another action that says login failed or something
SessionStore ---> Header Component (Listens to Store) --> Update view based on store information
---> Side Menu Component (Listens to Store) --> Update
speaking more general:
flux is a software architecture for a unidirectional Dataflow. It's Chain is Action -> Delegation -> Store -> View... The action - for example a Button Click - gets delegated to stores where your applicationlogic and data is kept... here your action and data will be changed and processed. The store eventually emits an event which views (for example react components) previously registered on with a callback. In this callback you can GET your data from your stores. It is important to mention that you can only access the stores READ-Only.
So for your case... if you want component A to affect component B you will have to register component B to the store eventEmitter and GET the desired data from the store. Once component a triggers an action it gets delegated to the store, your functions are performed and eventually the event gets thrown that starts component B's callback.
Hope this got clear enough... its way cooler with some nice drawings.