I've seen the following pattern a lot in primitive components (like Radix UI) where layouts are rendered as custom children components. For example instead of this:
<List
title="Hello!"
items={
['Item 1', 'Item 2', 'Item 3']
}
/>
the primitives will use this:
<List.Root>
<List.Title>Hello!</List.Title>
<List.Items>
<List.Item>Item 1</List.Item>
<List.Item>Item 2</List.Item>
<List.Item>Item 3</List.Item>
<List.Items>
<List.Root>
This will end up rendering some custom layout with styles, etc, like the following:
<div>
<span>Hello!</span>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
My question is: what even is the name of this pattern, and how would I achieve it using functional components? How would I pass states between each of the elements? I've looked at the source for some of the Radix components, but they all use some sort of abstracted primitives, and a lot of Contexts.
The example above is pretty simple, but you can imagine this being used for more complex layouts. For example, a layout that returns two columns, where instead of passing column1 and column2, you just pass <Layout.First>...</Layout.First> and <Layout.Second>...</Layout.Second>.
Any help would be appreciated!
It looks like this pattern is called a Compound Component. The key is utilizing react's Context API.
Here's a pretty good tutorial that goes into more depth on how to actually create a compound component: https://blog.cristiana.tech/building-compound-components-using-react-context-api-and-typescript
Related
Im new to React, and I have my first question about it.
I have been told that what comes inside the "return" (The red line in the image) is HTML and if I want to use JS I need to open { } and put the code inside it (Small white arrows in the image) so React "understand" its JS.
So again, only JS code will be inside { } How come the <li></li> (blue line in the image) doesnt need to be surrounded by `` and the p.name need to be $p.name ? Cause its inside the {} and anything inside it should be JS!
So if I do need to put HTML inside { } is should be with string interpolation!
What you write React is not HTML itself, it is called JSX, which is a kind of HTML with some differences.
That's why you put it at the begging of your example className:
<div className="ProductList">
instead of normal use normal class HTML
<div class="myClass">`
So, technically, what you are writing all the time in JavaScript.
You can read more in React JSX
According to React document regarding JSX syntax:
Any JavaScript expression will work between curly braces { }
This also includes JSX tag, which is internally a syntactic sugar for a function that create elements.
Since map() returns a shadow copy of the array, the result of the following:
{["1", "2", "3"].map(item => <li key={item}>{`Product ${item}`}</li>)}
Returns something close to this, except that map() also requires adding unique key attributes to the output elements:
{[<li>Product 1</li>, <li>Product 2</li>, <li>Product 3</li>]}
And that is rendered close to as follows:
<li>Product 1</li>
<li>Product 2</li>
<li>Product 3</li>
More about rendering lists in React: documents
So I've been trying to build a nice way to represent hierarchical data in my application. I've created a slide effect for some bootstrap badges I am using to display some sub-categories using AngularJS.
I've created (with some help from you stackoverflow-ers!) the following example which works for data of the type parent and child. ie. You can't be a parent and child at the same time, which is needed for my real world example.
<ul>
<li ng-repeat="category in categories"
ng-init="isChild = category.category_type == 'child'"
ng-show="category.category_show"
class="badge-slider">
<span class="badge {{ isChild ? 'badge-c' : 'badge-p' }}"
ng-click="isChild || updateResults(category)"
ng-bind="category.category_name">
</span>
</li>
</ul>
I am struggling to understand what is the best way to change the code so it is able to work for the additional 'parent and child' category_type. This 'parent and child' category_type is needed for creating a way to browse a directory structure.
In the example above, it relies on the boolean value 'isChild' and the ternary operator, which doesn't work when we introduce the 'parent and child' category_type.
Anyone got any ideas?
Here is a link to a PLNKR which demonstrates the sliding functionality working for simple the parent-child relationship: http://plnkr.co/edit/9CiXW1YAoPj80x6PtBW3
EDIT 1:
I've created another PLNKR to handle the 3-tier nature of the hierarchical relationship. It works fine, except it doesn't display 'parent and child' elements with the corresponding badge.... http://plnkr.co/edit/YoRI578GHE91t6torCUt
Here is how I solved this:
Modified categories data structure
Created inline angular template in html file
Create new CSS class for "parent and child" elements
You can view the working Plunker
Here is a Snapshot of the result:
Your data structure is not currently hierarchical. I would suggest a structure such as this for your categories list (probably a better name name), where each element in an array has an array "children" as a property.
$scope.categories = [
{category_name:"apples", children: [
{category_name:"bramble", children: [
{category_name:"red"},
{category_name:"green"}
]},
{category_name:"granny smiths"},
{category_name:"pink lady"}
]},
{category_name:"oranges", children: [
{category_name:"satsuma"},
{category_name:"nectarine"},
{category_name:"mandarin"}
]}
];
Here is a plunkr of an inefficient way to represent this (templates and directive would be a more effective way to work on the recursive aspect versus doing it manually).
Before anyone press eagerly the close button, I already have looked the following question: ReactJS Two components communicating. My problem is exactly the third scenario developped in the current accepted answer.
I am using ReactJS to build something with two components. For HTML reasons (and presentation), i want my two components to be at two different places of the page.
For the moment, I have the following pattern, corresponding to scenario #2:
FooForm = React.createClass({
...
});
FooList = React.createClass({
...
});
FooManager = React.createClass({
...
render: function () {
return (
<div>
<FooForm ref="form" manager={this} />
<FooList ref="list" />
</div>
);
}
});
React.render(
<FooManager someProp={value} />,
document.getElementById('foo')
);
This gives something like:
<div id="foo">
<form>Form generated with the render of FooForm</form>
<ul>List generated with the render of FooList</ul>
</div>
However, i would like to have something like this:
<div id="fooform">
<form>Form generated with the render of FooForm</form>
</div>
<!-- Some HTML + other controls. Whatever I want in fact -->
<div>...</div>
<div id="foolist">
<ul>List generated with the render of FooList</ul>
</div>
The problem here is: how can I keep a reference in each component? Or at least the link Form -> List?
I tried to create the FooList before and pass the reference to the current manager, but I get the following warning/error:
Error: Invariant Violation: addComponentAsRefTo(...): Only a ReactOwner can have refs. This usually means that you're trying to add a ref to a component that doesn't have an owner (that is, was not created inside of another component's `render` method). Try rendering this component inside of a new top-level component which will hold the ref.
The documentation says you can attach events to link two components which do not have a parent-child relation. But I don't see how. Can someone give me some pointers?
The Less Simple Communication lesson from react-training has a good example of how you can move actions & state sideways to avoid having to create an explicit link between related components.
You don't need to jump into a full Flux implementation to get the benefit of this approach, but it's a good example to lead you up to Flux, should you eventually need it or something like it.
Note that this requires you to model the relationship between the components based on changing state rather than explicitly passing a reference to a component instance (as you're doing above) or a callback bound to the component managing the state.
This would be the perfect use-case for a Flux type architecture.
What you want is someone FooManager to be able to trigger state changes in both components. Or, in fact, having the different components trigger, through Actions, state changes in each other.
The Flux Todo-App Tutorial illustrates your use-case perfectly!
After this, then you'd have the choices of using Facebooks implementation of Flux or the other gazillion ones.
My personal favorite is Reflux
I'm new to ember and am struggeling with the typical "how would one do that"-Problem. What I've got is fairly simple and I know how to do it, but my way is so complicated that I do not think it's correct.
The case:
<ul>
<li>{{link-to top-level}}</li>
<li>{{link-to another-top-level</li>
<ul class="submenu">
<li>{{link-to submenu</li>
</ul>
</ul>
What should happen is:
When a route is clicked, the corresponding list element should become active.
When a submenu is clicked the corresponding upper ul-element should get the class open
It's a fairly simple case with jQuery, but I understand that this is not scalable and abstracted and stuff.
Therefore I started with this approach:
Create a controller / template construct for the entire navigation to handle it's state (there are some other things I need to check as well, so it came in handy).
since ember adds the active class to the anchor tag I created a component to observe that:
Like:
export default Ember.Component.extend({
tagName: 'li',
classNameBindings: ['active'],
active: function() {
return this.get('childViews').anyBy('active');
}.property('childViews.#each.active')
});
Replacing the li elements with {{linked-list}} does indeed work.
But what next? Do I need to add another component to watch the component to watch the build in behaviour of active links? Do I have to write dedicated MVC-Classes for all the DOM Elements?
There has to be a simpler way, I think. I already created a whole lotta files for such a simple behaviour that I'm thinking I'm totally on the wrong track.
My gut feeling is: That is view logic and the view should just observe a few states in the template and that's it.
What's the leanest approach to the problem?
I don't know if I understand your question right, but why you want to add the class open to the corresponding upper element? It automatically get active assigned. And with correct CSS it should work as expected.
I have created a small example demonstrating what I mean. Please have a look and let me know, if that's the solution for you or what's your problem with this solution.
http://emberjs.jsbin.com/wifusosadega/7/edit
EDIT
Here is a Bootstrap flavored version: http://emberjs.jsbin.com/wifusosadega/9/edit .
I'm new to Jquery/javascript and I have myself a bunch of levels of links that I need to organize in to lists that are expandable and collapsible via a +/- button.
An example of the information I am working with is:
Category1 (Level 1)
- Subcategory 1 (Level 2)
- Subcategory 2
- Subcategory 3
Category2 (Level 1)
- Subcategory 1 (Level 2)
- Subcategory 2
- Subcategory 3
Category3 (Level 1)
- Subcategory 1 (Level 2)
----Subcategory 1 (Level 3)
- Subcategory 2
- Subcategory 3
Code wise, it looks like this:
<div class="navCol">
<h4>Pet Club</h4>
<ul>
<li>Pet Home</li>
<li>Arts</li>
<li>ns</li>
<li>Pet Prescrions</li>
</ul>
</div>
<div class="navCol">
<h4>Fresh Ideas</h4>
<ul>
<li>Know Yur Food</li>
<li>Depaments</li>
<li>Free</li>
<li>HoBQ</li>
<li>rt</li>
<li>Kidsb</li>
<li>Tr</li>
</ul>
</div>
<div class="navCol">
<h4>vings</h4>
<ul>
<li>w</li>
<li>Bb</li>
<li>asdf</li>
<li>On </li>
<li>C</li>
<li>Em</li>
<li>P</li>
<li>W</li>
<li>Pet</li>
<li>Fuel</li>
<li>School</li>
<li>Weekly</li>
</ul>
</div>
I want to be able to click the category +- and it will show/hide all the links underneath it. Same with subcategory1 in category3 where I am dealing with the third level. How would I be able to implement all the way down to level 3?
Should I precede all my links with L1, L2, L3 tags? I have looked at some questions that deal with the +/- sign and implementation but I do not understand a lot of it.
Also, how would I go about implementing a button that shows all or expands all on toggle? That is, it would expand to the deepest levels and collapse back to show only the level 1 categories.
If you any place where I can start to learn this on my own, that would be greatly appreciated as well. This is my first project and I am confused as to where to start.
You'll want to start by thinking about your HTML structure. You'll need some kind of nested elements to express the list. I would recommend using nested <ul> elements because it semantically represents your data structure.
Don't worry about how many levels there are; if you do it right, the same code will work for a collapsible element at any level.
You'll really have three tasks: knowing when the tree element is clicked on, finding its children, and toggling their visibility.
You can use jQuery's .click() handler to determine when the element is clicked on. Be careful, though: if you have, for example, nested <li> elements, and you use $('li').click as your event, you'll catch multiple clicks for sub-elements (as they will also trigger their parent's click event). So you'll have to be a little clever with your selector.
Once you're able to detect the click, then you can find any sub-items by using jQuery's .children() function.
Finally, it's a simple matter of toggling the visibility of child elements, which you can do easily with jQuery's .toggle() function.
Lastly, I agree with the sentiment of Josh's comment: why re-invent the wheel? If you're doing it to learn, that's great, and you can use the approach I outlined in this answer. If you just need it done, you should look into using an existing jQuery extension, like Josh's suggestion of Accordion or jQuery Treeview.
While I'd usually advise getting to grips fully with javascript before going head on with jQuery, the latter provides a much easier solution here.
Take a look at .toggle()
Edit: Josh's suggesting is a fine one, but it might be worth getting to grips with building simpler versions beforehand.
Here is a very simple example http://jsfiddle.net/xNh6R/4/
Javascript:
$('.level1').click(function(){
if($(this).next('div').is(':visible')){
$(this).next('div').hide("blind");
} else {
$(this).next('div').show("blind");
}
});
$('.level2').click(function(){
if($(this).next('div').is(':visible') && $(this).next('div').hasClass("level3")){
$(this).next('div').hide("blind");
} else if($(this).next('div').hasClass("level3")) {
$(this).next('div').show("blind");
}
});