how to insert data in React js - javascript

I am facing an issue with React JS , i want to do show all the data in a row not in a column.
What should I change in my code?
Working demo:
https://codesandbox.io/s/nostalgic-wood-9jrib?file=/src/App.js:0-355
react component
import React from "react";
export default function App() {
const names =
data &&
data.length &&
data.map(({ full_name }) => <p key={full_name}>{full_name}</p>);
return <div className="row">{names}</div>;
}

p : is being used as paragraph so it will cover whole width by default, until unless you override with css
You can use,
// if you need more space you can apply class to span and add some padding-mrgin
// as per your need
<span key={full_name}>{full_name} {' '}</span>
in place of
<p key={full_name}>{full_name}</p>

Related

How to dynamically import icon / jsx element by string?

I'm working on an icon selector at the moment. Once an icon is chosen, the selector returns the icon as a string, like AirBalloon. Then, I want to display that icon on my page, so I need to import it (I'm using https://www.npmjs.com/package/tabler-icons-react).
Normally, I would do it like import {AirBallon} from 'tabler-icons-react';.
So I tried this:
<IconSelector
active={iconSelectorActive}
setIcon={setIcon}
additionalEvent={async () => {
console.log(icon); // Logs the icon I selected
setActiveIcon((await import('../../node_modules/tabler-icons-react/dist/icons/' + icon)));
}}
></IconSelector>
But if I try to embed it into my JSX like that:
<Button
onClick={() =>
setIconSelectorActive(!iconSelectorActive)
}
variant="PRIMARY"
>
{activeIcon}
</Button>
It throws the error Error: Cannot find module './' when I click an item from the selector.
How do I fix this?
If you want to keep the dynamic import, you can use await import('tabler-icons-react') then select the icon you need from the default export dynamically.
<IconSelector
active={iconSelectorActive}
setIcon={setIcon}
additionalEvent={async () => {
console.log(icon); // Logs the selected icon
const icons = await import('tabler-icons-react');
const IconComponent = icons[icon];
setActiveIcon(<IconComponent />);
}}
></IconSelector>
First, you need to import all the icons from the library like this:
import * as allIcons from "tabler-icons-react";
And then retrieve the selected icon by using its name like this:
const iconNameToBeUsed = "Activity";
const IconToBeUsed = allIcons[iconNameToBeUsed];
Finally render the selected icon jsx like this:
<IconToBeUsed />
You can take a look at this sandbox for a live working example of this solution.

Next.js - Cant apply dynamic class names when using CSS Modules

hoping someone here can help me solve this.
Am trying to build a website through NextJs. One of my pages has some paragraphs and buttons which are styled differently based on states and events. I can get the styling to work as intended when using pure React, and also when using a Global Stylesheet with NextJs; but when I use CSS Modules I cant get it to function as intended.
(Note: I can also get it to work by using a simple ternary like
<h1 className={submitted ? styles.showresult : styles.hideresult}>Correct? {correct}</h1>;
but I have some other scenarios where I need to rely on an multiple ifs and create multiple classes, each with their own styling, so I cant make a simple ternary my final solution.
E.g. this is the file pagex.js
import React from 'react';
import ReactDOM from 'react-dom';
const Pagex = () => {
const [submitted, setSubmitted] = React.useState(false); // whether the submit button was pressed
function calculateScore() {
let correct = 0
let incorrect = 0
//......some scoring logic.....
setSubmitted(true)
}
// function to create a display class based on whether the submit button has been pressed
function displayResult(){
if (submitted === true) {
return "showtheresult"
} else {
return "hidetheresult"
}
}
return (
<section className="results">
<h1 className={displayResult()}>Correct? {correct}</h1>
<h1 className={displayResult()}>Incorrect? {incorrect}</h1>
<button className={displayResult()} onClick={handleMovClick}>An instruction</button>
</section>
</div>
);
};
export default Pagex;
the globals.css file contains
h1.hidetheresult, h3.hidetheresult {
visibility: hidden;
}
h1.showtheresult, h3.showtheresult {
visibility: visible;
}
button.hidetheresult {
border-color: pink;
}
button.showtheresult {
border-color: aqua;
}
Changes when using CSS modules
Add a CSS file in the correct folder with the correct name
(../styles/Pagex.module.css) which contains the same styling shown
above
Additional import in pagex.js import styles from '../styles/Pagex.module.css'
Change reference in the function
within pagex.js
function displayResult(){
if (submitted === true) {
return {styles.showtheresult}
} else {
return {styles.hidetheresult}
}
}
When i do this the '.' in {styles.showtheresult} and {styles.hidetheresult} gets highlighted as an error by vscode with this detail: ',' expected. ts(1005).
Saving the js with a dev server running shows a similar message after trying to compile: Syntax error: Unexpected token, expected "," and the browser shows the same message along with "Failed to compile"
Also tried just passing styles.showtheresult / styles.hidetheresult by removing the curly braces from the displayResult() function. That compiles but nothing happens on the compiled webpage, i.e the class doesnt get updated when the button is pressed and so the styling cant be applied.
Also Tried passing as ${styles.showresult} and ${styles.hideresult} (with `)in the return statement. That also compiles but the page itself gives me an "Unhandled Runtime Error ReferenceError: styles is not defined" message and I cant load the page.
Would highly appreciated if someone could help correct my syntax in the function itself or elsewhere in the code.
Because you asked nicely ;) (just kiddin')
So Next.js is an opinionated framework and uses CSS Modules to enforce component scoped styling.
Basically you define your stylesheet with a name.module.css filename and add regular CSS in it.
.hidetheresult {
visibility: hidden;
}
.showtheresult{
visibility: visible;
}
.btn-hidetheresult {
border-color: pink;
}
.btn-showtheresult {
border-color: aqua;
}
Now to use this, import it like any JS module,
import styles from './styles.module.css'
console.log(styles);
// styles => {
// hidetheresult: 'contact_hidetheresult__3LvIF',
// showtheresult: 'contact_showtheresult__N5XLE',
// 'btn-hidetheresult': 'contact_btn-hidetheresult__3CQHv',
// 'btn-showtheresult': 'contact_btn-showtheresult__1rM1E'
// }
as you can see, the styles are converted to objects and now you can use them
like styles.hidetheresult or styles['btn-hidetheresult'].
Notice the absence of element selector in the stylesheet. That's because CSS Modules rewrite class names, but they don't touch tag names. And in Next.js that is
the default behaviour. i.e it does not allow element tag selectors.
File extensions with *.module.(css | scss | sass) are css modules and they can only target elements using classnames or ids and not using tag names. Although this is possible in other frameworks like create-react-app, it is not possible in next-js.
But you can override it in the next.config.js file. (Beyond the scope of this answer)
There is an article which explains how to override it. - disclaimer: I am the author
Now coming to your use-case, you can do contitional styling like so: (assuming the styles are as per the sample given in the answer)
import React from "react";
import styles from "./styles.module.css";
const PageX = () => {
const [submitted, setSubmitted] = React.useState(false);
const getStyle = () => {
if (submitted) return styles.showtheresult;
else return styles.hidetheresult;
};
const getButtonStyle = () => {
if (submitted) return styles["btn-showtheresult"];
else return styles["btn-hidetheresult"];
};
return (
<div>
<section className="results">
<h1 className={getStyle()}>Correct?</h1>
<h1 className={getStyle()}>Incorrect?</h1>
<button className={getButtonStyle()} onClick={handleMovClick}>
An instruction
</button>
</section>
</div>
);
};
As you add more conditions, the methods do tend to get more complex. This is where the classnames
module comes handy.
import styles from "./styles.module.css";
import clsx from "classnames";
const PageX = () => {
const [submitted, setSubmitted] = React.useState(false);
const headerStyle = clsx({
[styles.showtheresult]: submitted,
[styles.hidetheresult]: !submitted,
});
const btnStyle = clsx({
[styles["btn-showtheresult"]]: submitted,
[styles["btn-hidetheresult"]]: !submitted,
});
return (
<div>
<section className="results">
<h1 className={headerStyle}>Correct?</h1>
<h1 className={headerStyle}>Incorrect?</h1>
<button className={btnStyle} onClick={handleMovClick}>
An instruction
</button>
</section>
</div>
);
};
Here's a CodeSandbox for you to play with:

How to calcuclate properties from element sizes in React?

I have a card on my page, which has a title that is the name of a user. The names are sometimes longer than the card, the overflow previously was ellipsis, and a tooltip appeared on hover to show the full name. Now it should be changed in a way, that if it would overflow, then the texts letter-spacing attribute would decrease all the way down to -2, and after that will it start to truly overflow with an ellipsis. How to calculate the values from the available width of the enclosing div? Or how else should it be impemented.
Current state:
For names that can fit with letter-spacing : -2
For long names :
For short names:
Note, container size is the same across all cards!
Let's assume your card component is like this
function Card({ name }) {
return (
<div className="card">
<div className="card__pic"></div>
<h3 className="card__name" ref={nameRef}>
{name}
</h3>
</div>
);
}
All we need is the element containing the name i.e "card__name".
Access the 'name' element.
To access this name element, we have to use useRef hook. Here's how you would use it.
function Card({ name }) {
const nameRef = useRef(null);
useEffect(() => {
// nameRef.current is accessible here
}, []);
return (
<div className="card">
<div className="card__pic"></div>
<h3 className="card__name" ref={nameRef}>
{name}
</h3>
</div>
);
}
We have to pass ref={refVariable} to the element we'd like to access.
The refVariable will be initialized with the element node after mount i.e we can use it inside the useEffect hook.
Determine if the name is overflowing.
Now since we have the element, we can use it to check if the text inside is overflowing. I've created a function isTextOverflowingCard to which we pass the reference to the element we get after mount. It will return true if it text is overflowing else false.
function isTextOverflowingCard(textElement) {
if (textElement.scrollWidth > textElement.clientWidth) {
return true;
}
return false;
}
Now inside Card we can use this function and based on its return value, add a class conditionally which will have the tight letter-spacing value.
function Card({ name }) {
const nameRef = useRef(null);
const [textOverflow, setTextOverflow] = useState(false);
useEffect(() => {
// nameRef.current is accessible here
setTextOverflow(isTextOverflowingCard(nameRef.current));
}, []);
return (
<div className="card">
<div className="card__pic"></div>
<h3 className={`card__name ${textOverflow ? 'card__name--tight-kerning' : ''}`} ref={nameRef}>
{name}
</h3>
</div>
);
}
In CSS,
.card__name--tight-kerning {
letter-spacing: -2px;
}
You can change useEffect to useLayoutEffect, which is better suited for such dom related calculations and applying to ui.
References:
Detect if text has overflown
useEffect vs useLayoutEffect
https://reactjs.org/docs/hooks-reference.html#useref

How to add text on border with css for input field using React JS?

I want to create an input box like below,
using css, however am not able to find anything on how to do the text on border for input fields anywhere at all
I tried using but am unable to create the input box as shown above.
I am very much stuck here, it would be a great help if anyone could suggest anyway to create an input field like shown above.
Thank you in advance.
You can use react-native-material-textfield library and need some customization. There is a prop renderLeftAccessory which can render your left view like country code here. Try example code here
import React, { Component } from 'react';
import {
TextField,
FilledTextField,
OutlinedTextField,
} from 'react-native-material-textfield';
class Example extends Component {
fieldRef = React.createRef();
onSubmit = () => {
let { current: field } = this.fieldRef;
console.log(field.value());
};
formatText = (text) => {
return text.replace(/[^+\d]/g, '');
};
render() {
return (
<OutlinedTextField
label='Phone number'
keyboardType='phone-pad'
formatText={this.formatText}
onSubmitEditing={this.onSubmit}
ref={this.fieldRef}
/>
);
}
}

Split string into separate line according to container size

I didn't find any proper solution inside old threads so...
I'm trying to replicate one of the feature of SplitText plugin of GSAP (here is the reference).
I'm building a simple Gatsby website to learn a bit about react.
I've a simple component that will be rendered into several instances inside index.js.
Here is the simplified code for feature.js
import React, { useState } from 'react'
import classnames from 'classnames/bind'
import styles from '../styles/modules/feature.module.scss'
let cx = classnames.bind(styles)
const Feature = props => {
const [state] = useState({
id: props.id,
subtitle: props.subtitle,
title: props.title,
description: props.description,
content: props.contentPosition,
image: props.imgPosition,
})
return (
<section className={cx('feature')}>
<div className={cx('featureContent', `${state.content}`)}>
<div className={cx('featureContentInner')}>
<div className={cx('subtitle')}>
<h5>
<span>{`${state.id}.`}</span>
{state.subtitle}
</h5>
</div>
<h1>
<div className={cx('contentLine')}>
<div className={cx('contentLineInner')}>Your peace of</div>
</div>
<div className={cx('contentLine')}>
<div className={cx('contentLineInner')}>mind in the</div>
</div>
<div className={cx('contentLine')}>
<div className={cx('contentLineInner')}>heart of Venice.</div>
</div>
</h1>
<p>{state.description}</p>
</div>
</div>
<div className={cx('featureImg', `${state.image}`)}>
<div className={cx('featureImgInner')}>
<img />
</div>
</div>
</section>
)
}
export default Feature
As you can see, I'm holding props inside my state and place them in my render methods.
The only thing that is hard coded is the props.title because I'm fetching a String and I need to split that String into several lines.
Since I've several instance with different props.title the length of each line should be calculated dynamically.
The structure could be replicated each time h1 > div*x > div but each time there could be a different x.
I'm asking to this board how would you tackle this or if there is a plugin or some script to make this simple instead of calculating tons of variables (getBoundingClientRect split join etc...).
Or maybe the only solution is to pay 99 bucks for a single shot on a plugin :P
Thanks!
For the sake of forthcomers:
Setup a reference:
let paragraph = useRef(null);
This will be assigned to the text we would like to spilt:
<p ref={el => (paragraph = el)}>
this is a simple paragraph that I'm trying to split
</p>
Next, we are going to setup an initial state based on how many max lines do you expect (don't worry if there will be less lines, like when resizing, they will be hidden):
const [content, setContent] = useState({
firstLine: '',
secondLine: '',
thirdLine: ''
});
Then simply execute the splitting once the component did render.
With array function you can go as deep as you want: in my example I need the content of each line. I'll hide the generated splitting from Splittext.js and inject the result of each lines into my preformatted code.
useEffect(() => {
// Split the paragraph into words and lines
const splittedText = Splitting({ target: paragraph, by: 'lines' });
// Get an Array of Lines that contains an array of Words
const lines = splittedText[0].lines.map(line =>
line.map(word => word.textContent)
);
// Get every single lines as an Array of Strings
const contents = lines.map(line => line.join(' '));
// Hide the original paragraph
paragraph.style.display = 'none';
// Change the content state
setContent({
firstLine: contents[0],
secondLine: contents[1],
thirdLine: contents[2]
});
}, []);
Then just render how many lines of how-nested-you-want and with how-many-classes-you-like divs

Categories

Resources