Mithril: Wrapping children in an array when using jsx - javascript

I'm so close to getting jsx to work with mithril.
However, mithril wants you to wrap child elements in an array. It doesn't show a jsx example more than one element deep in the mithril documentation.
So a mithril component such as
'use strict'
import m from 'mithril';
export default m.Component = {
view () {
return (
<div className={'xxxx'}>
<div className={'xxxx'}>
<div className={'xxxxx'}>
<h2>xxxxx</h2>
<p>xxxxxx</p>
<p>xxxxxx</p>
</div>
</div>
</div>
)
}
};
yields the error message
Mithril v1.0.0 only supports 3 arguments, wrap children in an array.
and the component fails to render. It worked when I just had the header and no paragraph elements. How can I wrap the three elements in an array in a way jsx will parse?

You can wrap divs and such in an array if you wrap them in curly braces, like so:
...
return (
<div className={'xxxx'}>
<div className={'xxxx'}>
<div className={'xxxxx'}>
{ [
<h2>xxxxx</h2>,
<p>xxxxxx</p>,
<p>xxxxxx</p>
] }
</div>
</div>
</div>,
)
...

What I was missing was the commas between list elements.
return (
<div className={'xxxx'}>
<div className={'xxxxx'}>
<div className={'xxxx'}>
{[
<h2>xxx</h2>, // Comma here
<p>xxxx</p>, // Comma here
<p>xxxx</p>
]}
</div>
</div>
</div>
)

Related

There is a problem with implementing the map function of react

I'm sorry to ask you a simple question first. But I don't know where to fix it. I wanted to create an array of characters like a picture, so I used the map function and array, but I can't apply which part of the code is wrong. Can I know which part is wrong and how to change it? Thanks.
Footer.jsx:
this is the file I used map function and array
const array = [
'Meta', '소개', '블로그', '채용 정보', '도움말', 'API', '개인정보처리방침', '약관', '인기 계정', '해시태그', '위치', 'Instagram Lite', '연락처 업로드 & 비사용자'
]
function Footer() {
return (
<FooterWrap>
<div className='FooterWrap2'>
<div className='FooterContent1'>
<div className='FooterContent1Wrap'>
{array.map((text, idx) => {
return (
<div>
<a>
<div>
{text}
</div>
</a>
</div>
)
})}
</div>
</div>
<div className='FooterContent2'>
</div>
</div>
</FooterWrap>
)
}
export default Footer;
Why not just use a tag and its href pointing to it's link
{array.map((text, idx) => {
return (
<a href="add_url_here" key={text}>
{text}
</a>
)
})}
If you have styling issues, you can add a codesandbox link, save code and share so one can look in to it

Add <br> tag using replaceAll() in javascript

I have this string:
export default function App() {
const string = 'test data,cars,colors,demo';
return (
<div className="App">
<h1>Hello {string.replaceAll(',','<br>')}</h1>
</div>
);
}
I expect:
test data<br>cars<br>colors<br>demo
But i get one string without breack inside my string. How to achieve what i expect using replaceAll()?
In order to display html from string you have to use dangerouslySetInnerHTML
export default function App() {
const string = 'test data,cars,colors,demo';
return (
<div className="App">
<h1 dangerouslySetInnerHTML={{ __html: `Hello ${string.replaceAll(',','<br>')}`}}></h1>
</div>
);
}
Assuming this is a React application you need to use dangerouslySetInnerHTML to add the new HTML to the page.
function Example() {
const string = 'test data,cars,colors,demo';
const html = `Hello ${string.replaceAll(',', '<br>')}</h1>`;
return (
<div className="App">
<h1 dangerouslySetInnerHTML={{ __html: html }} />
</div>
);
}
// Render it
ReactDOM.render(
<Example />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I don't suggest you dangerouslySetInnerHTML. It introduces both security and performance issues, and even if you are working on learning project, you better avoid such an approach.
The reason why your code does not work, JSX does not magically convert string "<br>" into appropriate element (until there is dangerouslySetInnerHTML, sure).
But JSX can render arrays just fine. It allows us to split initial string into elements: string.split(', ') and then inject JSX's <br /> with the help of .flatMap()(think about it as if .join() could return array with additional elements in between elements of source array).
{
string.
split(', ').
flatMap((el, index) => index ? [<br />, el]: el)
}
This approach is way more powerful than dangerouslySetInnerHTML since instead of simple BR you may use any JSX tree with React custom components, context and event handlers.
Another approach is to replace ', ' with newlines and apply CSS style white-space: pre-wrap(check white-space docs on all values available)

Dynamically made strings for id in JSX

I want to make id like this dynamically.
<div id="track_1"></div>
<div id="track_2"></div>
So I gave the id like this from parent component.
export default function Components(props) {
return (
<AudioTrack trackNum="1"/>
<AudioTrack trackNum="2"/>
)
}
then in my AudioTrack Component I got the trackNum and want to use like this
const AudioTrack = (props) => {
return(
<div id="track_{props.trackNum}" ></div>
);
}
Howeber it doesn't work.
Is there any good way?
Since the div prop isn't a constant string, you need {} to indicate an expression, and then either use + to concatenate or a template literal with ${}:
<div id={`track_${props.trackNum}`}></div>

Vue - dynamically create divs that use e.g v-on:click

I have something like that:
<div v-if="dataIsLoaded" v-for="(line, index) in json.data" v-on:dblclick="edit(index)" v-html="test(index)">
</div>
and test(index) returns html-ish string:
<div id=${index} v-on:click="remove(index)"></div>
How can I make it work?
My goal is:
If dataIsLoaded == true, then foreach (line, index) in json.data perform
test(index) and return its output to that container and display it as html.
Meanwhile output from test(index) can have different functions/events associated with them defined via string (as I showed above).
Why are you using v-html here? v-html should really only be needed in very specific and limited situations; I don't think it applies here. The string <div id=${index} v-on:click="remove(index)"></div> is not plain HTML, it's a Vue template string, so it won't work. v-html is also susceptible to XSS attacks; all in all I say avoid it like the plague.
Do something like this instead:
<template v-if="dataIsLoaded">
<div v-for="(line, index) in json.data" #dblclick="edit(index)">
<!-- Type1 -->
<div v-if="line.type === 'Type1'" :id="index" #click="remove(index)"></div>
<!-- Type2 -->
<div v-else-if="line.type === 'Type2'">...</div>
</div>
</template>
I've added the type property on each line object as a discriminator to determine which template should be used for the line.
Also I've hoisted the v-if="dataIsLoaded" into a <template> above the div because v-if is evaluated for each div generated by the v-for and the condition doesn't depend on the children so it needn't be repeated for each child (a minor optimization).
If you don't like the idea of having lots of v-if and v-else-if (a sort of "switch" statement in the template) then you can use <component :is="..."> instead:
<div v-for="(line, index) in json.data" #dblclick="edit(index)">
<component :is="line.type" :id="index"/>
</div>
import Type1 from './type1.vue'
import Type2 from './type2.vue'
export default {
components: {
Type1,
Type2
}
}
It's not possible with v-html, as the doc states
Updates the element’s innerHTML. Note that the contents are inserted
as plain HTML - they will not be compiled as Vue templates
A more component-thinking solution is to create a component(s) with needed behaviour and paste them as a results of v-for:
// componentWithOnClick.js
<script>
export default {
name: 'ComponentWithOnClick',
methods: {
remove() {
//
}
}
</script>
<template>
<div id=${index} v-on:click="remove(index)"></div>
</template>
and then use it in the parent file:
//app.js
import ComponentWithOnClick from './ComponentWithOnClick.vue'
{
components: {
ComponentWithOnClick
}
//
<div v-if="dataIsLoaded" v-for="(line, index) in json.data" v-on:dblclick="edit(index)">
<ComponentWithOnClick></ComponentWithOnClick>
</div>

How to apply rules to only certain elements in array

I have this loop in react:
<div>
{this.state.dealersDeck.map(function(card, index){
return <div className="cardFormatDH" key={ index }> {card.rankKey}{card.suit} </div>;
}, this)}
</div>
This goes through an array of objects and then renders them on screen. This is all good except I would like to format it so I only display the contents at certain points. i.e. I'm creating blackjack and I don't want the dealers second card to be visible until the end.
I may have to show more code but was wondering if map had some sort of attribute that I could use.
You could add a boolean prop to each card and render based on that:
<div>
{this.state.dealersDeck.map(function(card, index){
return { card.display &&
<div className="cardFormatDH" key={ index }>{card.rankKey} {card.suit} </div>
}
}, this)}
</div>
You can use basic If...Else statements inside map function as well. Moreover you can write more business logic also to add more functionality.
var cardsHTML = this.state.dealersDeck.map(function(card, index){
if(condition...1){
return <div className="cardFormatDH" key={ index }> {card.rankKey}{card.suit} </div>
}
if(condition...2){
return <div></div>
}
}

Categories