I've designed a pretty simple responsive layout (be sure to open link with Chrome) and now I'm trying to implement it in React. I've attached some screenshots of the layout as well.
This app is supposed to be the typical tab/nav app, but due to the responsive layout, the view hierarchy is pretty awkward to work with in React.
I've built a React component called Layout to abstract away all the layout stuff. Layout also has props for renderLeft, renderRight, title, and onTab. Layout's children are then rendered into the content block.
The easy solution would be to make Layout a child of the current view and render whatever you want. However, this is going to mess up some animations I had in mind. I want to have a CSSTransitionGroup element wrapped around the tabbar, the title, and the left and right buttons animating them as they change. Thus, the Layout element must remain the same between the views else a new CSSTransitionGroup element will be rendered for each view which isn't good.
So now the problem is that I have a Layout component with a view rendered as its child (but sort of as sibling with respect to the App component), but the view needs to specify renderLeft, renderRight and title for the Layout which is its parent! For example, in the top-level App component, the render function may look like the following, and I need some way of setting Layout's renderLeft from the View.
render() {
return (
<Layout renderLeft={??}>
<View setRenderLeft={??}/>
<Layout/>
)
}
The only thing I've thought of so far seems totally like the wrong way of doing it:
In the top-level App component, have a state variables for renderLeft, renderRight, and title and App passes those to the Layout props. Now for the view, pass some functions like setRenderLeft, setRenderRight, and setTitle which will change the App state and thus change the Layout. We can thus call these functions in componentWillMount for each view.
This just seems like a total hack and seems to break the whole idea of one-directional-data-flow. However, I'm not sure how else to do it! Are there any more proper ways of doing this? I'm reminded of the concept of delegation when building iOS apps, but thats very OOP and not very FP.
Any ideas?
Your question is a little difficult to tell exactly what the issue is, however if I were you I would read up on https://facebook.github.io/react/tips/communicate-between-components.html
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.
Flux pattern is one of the possible ways to arrange this.
You want to either have the siblings talk to the parent and communicate there, or at the global level.
Hope this helps.
Related
I have a TabView component that has multiple tabs containing components. These components have entire hierarchies of other components. How could I know from any child component nested arbitrarily deep in one of these hierarchies whether or not it's parent tab is in focus in the TabView?
Preferably, I would want to implement this similar to react-navigation's withNavigationFocus (or an equivalent hook) so that any component can know if it's in tab focus without having to pass props down the chain of components. I'm thinking you could have a TabViewContext that components can register themselves as focus listeners to by doing a useContext. The TabViewContext would be provided by the TabView and the TabView would be responsible for determining what registered listeners are in focus when tabs change. My dilemma is I don't know how the TabView could determine efficiently what nested child components come into focus when the tab changes. Any ideas?
In case the other parent tabs are hidden, you could test for visibility in plain JS, rather than have a much more complex solution...
Checkout this answer on how to do this.
So components that care about the visibility of their parent tab could use a ref for their own DOM elements and test whether they're visible or not. You could build this into a simple helper function or a hook
EDIT:
I'd suggest going with something like this:
Each Tab will provide a context with method for any descendant to register a callback that will be called when the Tab is hidden. The TabView can pass a "isVisible" prop to each tab (if it doesn't already), so Tab can know when its display changes.
When a Tab changes from visible to hidden. All registered callbacks will be called.
I would of course write a hook or a helper function to create this TabVisibilty context so each Tab component can use it in a reusable manner.
I've posted this on Reactiflux but may get more traction here. I've created a sandbox to demonstrate my need: https://codesandbox.io/s/xvnk0znw7z
What I'm trying to do is create a generic modal wrapper that I can use to encapsulate several components; in the sandbox, the component is a date range picker, but I also have a formik component for example.
The thing is, the Apply and Clear buttons need to be a part of the wrapping modal, and not the child component it wraps.
However, when clicking on Apply or Clear, saving the child component state to the Context (not included for brevity) is really the responsibility of the child component itself; only it knows how it should do it.
In the example above, that would be the ok and clear methods of DatesPicker. Intuitively, I would call children.ok and children.clear from the wrapping modal, but I know for a fact that's not how it should be done.
I looked a lot at the Cat and Mouse render-prop example in React's docs, but I can't get my head around how I can apply this here.
i like to develop a simple tooltip component in react.js
the tooltip gets defined like so in e.g. App.jsx:
<TooltipLink>Hover over me
<Tooltip>I am the Tooltip content</Tooltip>
</TooltipLink>
My question is: What is the best way of TooltipLink and Tooltip to talk to each other?
They are nested but I cannot use props because they are not nested directly in the component itself. Also, I don't want to use the parent (e.g. App.jsx) to manage the communication between TooltipLink and Tooltip because I want them to be self-contained.
I thought about refs but when i define a ref inside the Tooltip component then TooltipLink does not know about it (I assume because refs only work when components are nested inside the components themselves).
I could of course use simple DOM-programming for TooltipLink and Tooltip to communicate (e.g. use e.target when the user hovers over TooltipLink and then find its first child) but I thought there must be a more react-y way...
You have 3 options:
cascading props in the whole hierarchy
using redux
using the context API
I prefer to use redux, because the context API is not officially supported (breaking changes may happen, like in react 16.3). Cascading props is not really beautiful but may be convenient for small apps. (<MyComponent {...props}>)
According to the docs, one should avoid having multiple components with state. I am in the situation where I want to make a text box that automatically expands vertically as the user writes, and for that I'm using this trick http://www.impressivewebs.com/textarea-auto-resize/, which means I need to get the height of a component. Now, I've been playing around with it a bit, and it doesn't seem feasible to pass a ref to my parent component which contains state, so the easy way out would be to keep a piece of state in the component with the textbox, and then use the ref from there.
This got me thinking, how exactly do multiple state components negatively affect my app? Is it only maintainability / comprehensability? Or are there actual performance issues with it? I've noticed a lot of open source react components that you would just plug in to your app keep state, meaning if you use open source components, chances are you will have several state components in your app.
It's totally ok to use local state for this kind of tricks on DOM. It's even better approach, than to share such implementation details to parent components.
In general, use this places for state:
Application-wide data in stores outside React (redux, flux-store, observables)
Form temporary data can be placed in store also. But if don't need it anywhere else except form, it's better to place this data in form component.
Tricks on DOM, short living and very local state can be placed in component that just need it
are there actual performance issues with it?
No. If you'll place all your state in components, your application will become even faster. Because when you update local state, only this component and it's childs updates.
But you shouldn't do that, because it kills maintainability.
lot of open source react components that you would just plug in to your app keep state
If component doesn't allow you to control it through the props - it's bad component. Usually open source components written to be easier to use, so they provide nice defaults, that allow you to just place component to your application, and be happy with that.
For example, Tabs component usually controlls selected tab using local state. But also it takes selectedTab and callback onSelect, so you can control it by yourself.
Stupid components (like your textarea component) should not have state with data. But they can have their own UI state.
In this case you can easily keep textarea height in state of stupid component.
Quick question about good form in a React.js app:
I have a top-level App that has a state like "selectedItem", and want a ListItem component to be able to change that state.
So you make a top-level method on App like selectItem: function(item) that does a setState(selectedItem: item).
App passes this function down as a property to List component.
List component passes this function down as a property to ListItem component.
Finally ListItem component uses it as the onClick handler, with a .bind(null, item) so that the top-level state can be changed by a clicked item in the list.
Is this right?
It seems quite messy. Seems it'd be nicer for the ListItem to be able to call App.selectItem(item) directly without it having to be passed down three times.
If ListItem is directly aware of App and its selectItem method, then ListItem is coupled to that application—it can never be reused in any other context. For components to be reusable, they should generally take a change (or whatever) handler as a property that it will call when appropriate.
As for components that are specific to your application (that is, they're not meant to be reusable), all that passing around can get messy. There's an architecture called flux that allows application-specific views (known as "controller-views") to interact with an application-wide dispatcher in much the way that you mention.
In short, reusable components should be passed props to make them maximally portable; application-specific components can access application-level logic directly, though many people still recommend passing properties as it makes data flow clear and explicit.