react: How to structure components - reactjs

I am taking up react, and need some advice on how to structure my component tree.
I want to build a generic List with filters associated with it
I have:
List component: displays a list of item. Props:
Children: prop(a function) used to render each item of the list
(item) =>
<li className="list-group-item" key={item.id}>
{item.name}
</li>
list: array of object
Filter Component: bunch of input/select used to filter the list
filterList: array containing all the field names
onChange: function to call when field changes
stateValues: values of the fields (state in the above component)
ListAndFilterContainer: contains the state the filters and passes it to the list
list: list of items passed to the list component
children: function passed on to the list as children to display the items
filterList: list of fields passed to the filter component
Render of the ListAndFilterContainer:
`<div>
<FilterOnList
filterList={this.props.filterList}
filterState={this.state.filter}
onChange={this.handleFilterChange} />
<ListComponent
list={this.props.list}
title={this.props.title}
>
{this.props.children}
</ListComponent>
</div>`
Now to make it generic I want to be able to modify the markup on how the list is gonna be rendered, and how the input fields of the Filter component are gonna be rendered.
I want to be able to use the logic of a filtered list but I still want to be free on the presentation.
How could I structure it ? The easiest way I guess would be to inherit the List component and alter the render ? For example creating a CarList extends List ?
Same for the Filter component ?
Thanks

A few thoughts...
Good idea to use a container component to hold the state. This a pattern that works well for React.
It sounds like the ListComponent could just have different types of children and might not need specific handling for different list types. In other words, you might have an array of CarItem components and render it as a children of ListComponent, instead of making a separate CarListComponent. It depends on whether you really need different logic for rendering the different lists. Some visual differences, e.g. smaller row heights, can just be accomplished with CSS, so if you pass in a className prop and render it like <ul className={this.props.className}> that can handle a lot of customization.
If you do find you need separate classes for different types of list components (or items, etc) I would avoid use of "extends", mixins, and inheritance in general. This is controversial, perhaps, but IMO, Javascript on its own is too loosely typed to work with inheritance. You lose track of what is happening when things get a little complicated, e.g. "Which class did I get that function from?" If you want to use inheritance, consider typescript. Otherwise, I'd go with functional composition - just import the functions you need from modules.

Related

In React, is it possible to pass props & function calls between a parent component and its children when they're rendered between its component tags?

I've been using Stack Overflow forever, but I just made an account to ask my first question. Thanks in advance for all your help. Here goes:
I'm trying to work out the proper composition for these custom modular table filtering components I wrote and I could use a little guidance.
My table rows are stored in an array in the main page that these components will be displayed on. Each <TableFilter /> takes a specific column name of the table as a prop and renders UI to input and select filter options (e.g. for numeric columns there's a button that lets the user select <, >, =, etc.) and has a function to apply that filter and return only the selected rows from the table.
I've got these TableFilter components working great independently, but I'd like to be able to apply multiple filters at once. That's why I am working on a TableFilterGroup component that will contain the filters as children and handle passing data between them.
The plan is for easy use of the component, formatted like so:
<TableFilterGroup rows={this.state.tableRows} apply={this.applyFilters} >
<TableFilter column={'Name'} isNumeric={false} />
<TableFilter column={'ID'} isNumeric={true} />
<TableFilter column={'Color'} options={['Red', 'Blue', 'Green']} />
</TableFilterGroup>
I intend to have one "Apply Filters" button, rendered by the TableFilterGroup, that will iteratively call the filter functions of each of the TableFilter child components.
(^ Edit for clarity: this ApplyFilters() function of the TableFilterGroup will get the selected rows from filter 1, then pass them into filter 2 and so on, then finally call this.props.apply to set the fully-filtered rows to a state in the main page that holds these components as well as the actual table where the data is displayed.)
All of the TableFilter components need to work on the table data provided by the rows prop of their parent TableFilterGroup, and the TableFilterGroup needs to be able to iteratively call the filtering functions of each of its TableFilter children, letting their output (the filtered rows) waterfall to apply multiple filters simultaneously.
I've been reading about useRef and all the examples seem to be for the case of the child component being rendered in the return statement of the parent component, but I'm looking to add them in as children in between the two tags of the TableFilterGroup.
If anybody reading this has experience with this type of composition and is able to point me in the right direction, I would very much appreciate it! Or, if the consensus is that I'm just structuring this incorrectly overall, what would you suggest as a better way to format it? Thanks!

ReactJS wrap children, and descendents, see-through a component

I'm trying to find a good way to produce a layout control that wraps all it's DOM children, let's say in a div tag for simplicity. The basic approach is to use React.Children.map, but this is posing some problems with constructed lists of children via components -- it cannot see inside Components.
For example, say I have this structure, where the final DOM result should be a <ul> with several <li> elements. The Item* components themselves do not know they are in a list.
<WrapItems>
<ItemA/>
<ItemB/>
<ItemC/>
<ItemD/>
</WrapItems>
I can produce the DOM result I want by iterating the react children and wrapping them in a li. However, some of these items are shared between several controls, motivating me to create a common component.
<WrapItems>
<ItemA/>
<CommonItems/>
<ItemD/>
</WrapItems>
Where CommonItems ends up rendering:
<>
<ItemB/>
<ItemC/>
</>
The issue is that I cannot wrap ItemB and ItemC anymore, since React.Children will walk iterate the immediate children*.
Essentially I want CommonItems to be transparent and expose it's children directly to the WrapItems control, so that it can wrap them correctly. Is there a way to do this?
I've tried an approach where I replace the component with a useCommonItems function that returns an array of items instead. In some cases this can work, but in others it becomes a challenge, and a performance concern. Some of these parts are conditional, and the use approach forces them to all end up non-conditionally evaluated, along with all their use... functions in turn.
*Note, I'm aware that React.Children.map does not iterate over fragments, but I can solve that part by doing my own Children.map that descends into fragments. The issue in this question is about iterating over the children of components. The solution may be related, I'm not sure.

How can i pass a filtered data into another sibling component in React?

I have multiple places where I need to filtered out different data, I decided to create a search component that can receive dynamic values through props. The new array coming from the search component should be pass to a new component.
I create a dummy example like this one :
<SearchBar placeholder='Search project by company name' filterFunc={'pass the function that get the input value'}/>
<TableData data={'new array coming from search component'}/>
I try to use the useState hook to store the filtered data but I did not succeed.
Can you please help me with a possible solution ?

React able to render arrays of elements without using .map?

I'm going through a book to better learn React and came across a surprising example. I previously thought that in order to render an array of anything in React, we need to map each element of the array, and pass them keys (I know keys aren't absolutely mandatory but a best practice so React knows which element to re-render upon change.)
But here is a working example of an array being rendered without needing to do anything special to it:
const Tail = ({number, children}) => (
<div>
Last {number} children:
{React.Children.toArray(children).slice(-number)}
</div>
)
Is this something special about children? Why is React able to render an array like this? There aren't even keys specified!
So React.Children provides utilities like toArray, map, forEach etc for dealing with the children props data structure.
In this case the toArray:
Returns the children opaque data structure as a flat array with keys assigned to each child. Useful if you want to manipulate collections of children in your render methods, especially if you want to reorder or slice this.props.children before passing it down.
Here are the docs if you want to read more: https://reactjs.org/docs/react-api.html

Reusable components structure (react native)

I want to seek advice regarding reusable components structure in react-native. I wanted to make them lean and adaptive. What I thought was to have generic components as wrappers and then have specific components using those wrappers. e.g. For products carousel I pass products data to Carousel component (just a FlatList) that renders Card component multiple times that has products details and product related icons. But what If I want to have categories or anything else inside card?
What I thought is to make card content passed as props
<Carousel>
{.... // ProductContent}
</Carousel>
<Carousel>
{.... // CategoriesContent}
</Carousel>
But it seems like I am over complicating things as I'll pass data to carousel, then carousel will pass it to card then card will pass it back to my content and carousel is mere a Flatlist and card is mere a TouchableOpacity. And also it will not look clean as I will have to define the content wherever I am using the Carousel. Why not just create two separate carousel components
<ProductCarousel />
<CategoriesCarousel />
Similarly I have a <PopUpModal /> component. which I am using for showing product details. Should I pass product content as children to keep content generic or just create <ProductDetailModal /> as a component and create more modals if required
So the point is whether to have specific bits and pieces of the app as components so that connecting them will complete the puzzle or to have generic customizable wrappers like components. Or something in between
I recommend atomic design.
Its hard to explain it here, so Ill leave a link.
https://bradfrost.com/blog/post/atomic-web-design/
The key point is to break(modularize) everything into tiny, replacable & reusable bits, and actually reusing and replacing them.
Another important, yet often neglected point is that separating smart and dumb components.
https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0
This is surely neglected and also seems cumbersome to aplly, but will simplify the codebase by detaching view related logic to data(api) related logic.
So those are the two rules I try to stick to.
P.S. Just as im_baby has pointed out in the comments, there is no answer, and we all compromise at some point. So try to be long sighted and practical, dont be dogmastic to rules, neither be short sighted and mess up the overall code quality and structure for immediate comfort
You will make a component like this giving the parent component all the liberty to change it through props.
render() {
const { all the props that you want to give your component} = this.props;
return (
<Carousel>
{this.props.childern} // like this you can design your one carosal and cahnge the data/view in the carosal.
</Carousel>
);
}
Then in your parent component you need to import this component like this:
import CarouselComponent from '../CarouselComponent'; //path to your component
<Carousel>
<View>
//any view you want to be rendered in the modal
</View>
</Carousel>

Resources