📜 ⬆️ ⬇️

We add cycles and conditions to jsx

If you use the React library, then you probably also use jsx. Of course, this is not necessary, and you can only write js using React.createElement , but with jsx it will turn out much more concise, which increases readability. And everything is great before the first need to display the data in a loop. In jsx cycles are not provided. But the insertion of js-code is provided. And here the question of readability arises again, but now it is significantly deteriorating. There is a situation when html is written in js-code, in which js-code is written with html. Of course, you can select html in a separate function. But then html will appear in the code here and there. And I would like to localize everything in one place. Fortunately, in modern javascript for almost any problem, there is a solution in the form of a library or a plugin. The above problem is easily solved by the babel transform-react-statements plugin.



For loop


The principle of the plugin is simple. The plugin converts jsx components, such as <For/> , to js code. Suppose there is such a component:

 const MyComponent = props => <ul> <For each="item" in={props.items}> <li key={item.id}> {item.text} </li> </For> </ul> 

After processing by the plugin, we get:
')
 var _this = this; const MyComponent = props => <ul> {Array.prototype.map.call(props.items, function (item, index) { return <li key={item.id}> {item.text} </li>; }, _this)} </ul>; 

Now more about For . The first attribute is in . This is a required attribute that indicates how to get an object to be iterated (for example, a variable). The value must be an expression, i.e. enclosed in braces.

The attribute each specifies the name of the variable for each element of the array. It is not mandatory. In case of its absence, the elements of the array will be transmitted as a spread-attribute.

 <div> <For in={items}> <Item /> </For> </div> 

Converted to:

 <div> {Array.prototype.map.call(items, function (value, index) { return <Item {...value} />; }, this)} </div> 

Also, as can be seen from the examples above, the element number in the index variable is available in the loop. You can rename a variable using the counter attribute:

 <For each="row" counter="rowIndex" in={rows}> <div key={`row-${rowIndex}` className="row"> <For each="cell" counter="cellIndex" in="row"> <div key={`cell-${cellIndex}`} className="ceil"> { cell.content } </div> </For> </div> </For> 

Key attribute


For React to work correctly, each element in the arrays must have a key attribute. It can be specified obviously, as in the example above. Another way is to use the key-is attribute. This may slightly improve readability. You can also specify keyIs in the plugin parameters. Then key will not need to be written in the template - the logic of its receipt goes into the business logic.

.babelrc
 { plugins: [["transform-react-statements", { keyIs: "id" }]] } 

 <div> <For each="item" in={array}> <div>{ item.value }</div> </For> <For each="item" in={array} key-is="someKey"> <div>{ item.value }</div> </For> <For each="item" in={array}> <div key={item.getKey()}>{ item.value }</div> </For> </div> 

Will be converted to:

 <div> {Array.prototype.map.call(array, function (item) { // key     return <div key={item.id}>{item.value}</div>; }, this)} {Array.prototype.map.call(array, function (item) { // key -   <For /> return <div key={item.someKey}>{item.value}</div>; }, this)} {Array.prototype.map.call(array, function (item) { //   React  return <div key={item.getKey()} key={item.id}>{item.value}</div>; }, this)} </div>; 

If condition


This is just an alternative for syntax:

 <div> { condition && <Component /> } </div> 

The main task is to do everything in a single, html-like style. There are two attributes: either true or false , checking the condition for truth or falsity, respectively. For several child elements, the condition will apply to each of them:

 <div> <If false={someCondition}> <div>  1 </div> <div>  2 </div> </If> </div> 

Converted to:

 <div> { !someCondition && <div>  1 </div> } { !someCondition && <div>  2 </div> } </div> 

Switch..Case..Default


Switch behaves the same way as in javascript. The Switch component has a value attribute, the value of which should be an expression in curly brackets, and Case child components, with their own value attributes. If the value does not match any of the Case values, the Default block is displayed. If the Default block is not present, null returned.

 <div> <Switch value={x}> <Case value={“foo”}> <div> Text 1 </div> </Case> <Case value="bar"> <div> Text 2 </div> </Case> <Case value={1}> <div> Text 3 </div> </Case> <Default> <div> Default text </div> </Default> </Switch> </div> 

Component


Quite a specific expression. Turns the contents into an arrow function:

 <Component> <div> text </div> </Component> 

Converted to:

 props => <div> text </div>; 

Accordingly, the variable props is available inside <Component/> , which can be overridden through the props attribute:

 <Component props="item"> <div {...item} /> </Component> 

At the output we get:

 item => <div {...item} />; 

Automatic wrapping


Suppose there is such a component:

 class MyComponent extends React.Component { render() { return <For each="item" in={props.items}> <div key={item.id}> {item.text} </div> </For> } } 

For will be converted to an expression that returns an array. However, the render method should return a React element. In order to use such a component, the loop must be wrapped in an element. For example:

 class MyComponent extends React.Component { render() { return <div> <For each="item" in={props.items}> <div key={item.id}> {item.text} </div> </For> </div> } } 

But this is not necessary. Since the plugin itself will wrap the array in the react-element. By default, this is <span /> . This behavior can be changed by specifying the wrapper parameter in the plugin settings:

 { plugins: [["transform-react-statements", { wrapper: '<div class=”wrapper” />' }]] } 

You can also disable automatic wrapping using the value of the no-wrap parameter:

 { plugins: [["transform-react-statements", { wrapper: "no-wrap" }]] } 

Turn off and rename expressions


Suppose that the project already has a component <If /> , which copes with its task. Then it can be disabled using the disabled parameter:

 { plugins: [["transform-react-statements", { disabled: ["If"]}]] } 

Or you can rename the expression to use a different name in jsx, for example, IfStatement :

 { plugins: [["transform-react-statements", { rename: { "If": "IfStatement" } }]] } 

The code is written for people, so its readability is so important. And readability is the main problem of jsx, and specifically - mixing of two languages. As you can see, this problem is solved, and it is solved due to the flexibility of jsx in the ability to embed javascript code.

PS The author would be pleased to receive stars on github , as a thank you for the work.

Source: https://habr.com/ru/post/330172/


All Articles