I have a function which I need to return a string with line breaks, or divs that will render properly
export const showPeople = (peopleArray = []) => {
let peopleString = peopleArray ? peopleArray.map((people) => {
<div>
`${people.name && people.name},
${people.last_name && people.last_name}`
</div>
}).join('') : ''
return peopleString
}
I tried with divs, without divs, with return without return, and without join and i get [Object] for each person, in this particular case an empty string
I have seen similar questions but was not able to solve my problem
If the goal is to reduce a list of items to a single string that is rendered as a multi-line string, then you can use the white-space:pre-wrap; CSS rule to achieve this:
body {
white-space: pre-wrap;
}
Once you've done that you should be able to render a multiple line string with line breaks on \n characters, as follows:
export const showPeople = (peopleArray = []) => {
let peopleString = peopleArray ? peopleArray.map((people) => (
`${people.name ? people.name : ''} ${people.last_name ? people.last_name : ''}`
})
.join('\n') : ''
return peopleString
}
I've posted a working version here for you to see also
Hope this helps!
You dont want to return a string you want to return an array of elements:
let peopleString = peopleArray.map((people) => (
<div>
{people.name || ""},
{people.last_name || ""}
</div>
));
return peopleString
export const showPeople = (peopleArray = []) => {
let peopleString = peopleArray ? peopleArray.map(people =>
`<div>
${people.name}, ${people.last_name}
</div>`
).join('') : ''
return peopleString
}
var peoples = [{name: 'Jhon', last_name: 'Doe'},{name: 'Mario', last_name: 'Peach'}]
var peopleString = peoples.map(people =>
`<div>
${people.name}, ${people.last_name}
</div>`
).join('')
console.log(peopleString)
document.getElementById('container').innerHTML = peopleString
<div id="container"></div>
Since you're not doing string concatenation with variables. You need to remove Template Strings inside your divs.
It appears to me that you are not returning anything in that map function:
(people) => { ...string here... }
I would suggest, simply:
(people) => ( ...string here... )
or:
(people) => { return ...string here... }
Another issue might be that what you seem to intend to return in the map function is NOT a string, and probably(?) should be; it's hard to say without seeing the function in context.
So if you specifically want to use the HTML Line Break element to separate the strings you can use this.
import { Fragment, ReactNode } from "react";
const separateWithLineBreaks = (strings: string[]): ReactNode => {
return (
<Fragment>
{strings.map((str, index) => (
<Fragment key={index}>
{str}
{index < strings.length - 1 && <br />}
</Fragment>
))}
</Fragment>
);
};
Related
So I am trying to return the data from API by map and when I am using ( ) these brackets I am getting the data when I use { } to put if statement, I am getting nothing on my web page but still getting the data in console
const Addtocart = () => {
const data = useSelector((state) => state);
console.log("products", data.productData);
const dispatch = useDispatch();
useEffect(() => {
dispatch(productList());
}, []);
return (
<div id="addtocart-info">
<div className="products">
{data.productData.map((item) => { // here this bracket
if (item.id % 2 === 0 || item.id === 0) {
<div key={item.id} className="product-item">
<img src={item.photo} alt="" />
<div>Name : {item.name} </div>
<div>Color : {item.color} </div>
<button onClick={() => dispatch(addToCart(item))}>
ADD to Cart
</button>
</div>;
console.warn(item.id);
} else {
console.log(item.id);
}
})}
</div>
</div>
);
};
export default Addtocart;
Is there any way to put if statement with () or make this work
You are not getting anything because when u use {} you have to use a return keyword, but when you are using () you don't have to use a return keyword because the whole code inside this is considered as a single piece of code even if it's distributed in multiple lines
so change your code to ,
{data.productData.map((item) => { // here this bracket
if (item.id % 2 === 0 || item.id === 0) {
return (
<div key={item.id} className="product-item">
<img src={item.photo} alt="" />
<div>Name : {item.name} </div>
<div>Color : {item.color} </div>
<button onClick={() => dispatch(addToCart(item))}>
ADD to Cart
</button>
</div>
)
} else {
console.log(item.id);
}
})}
If you use curly brackets you also need to use a return statement. Basically if you don't use curly brackets in an arrow function the statement is returned automatically.
Example:
let x = someArray.map(x => x*2); // returns a new array with the expression applied
let x = someArray.map(x => {return x * 2}) // use the return here
I'm trying to do a little react app pulling some uniswap crypto data for my own UI just for fun, I've grabbed some data with a graphql query and I'm trying to render it out on the condition that its loaded which I get from a ternary operator in my functional component.
when I try this in multiple combinations, I just get the error that allTokenData.map is not a function
I have included my component below and I have notated where my mapping function is trying to pull data from the array I get back from graphql, since I'm getting data I'm sure I'm just mixing something up with the mapping syntax :/
here is a snippet of the data I'm grabbing for reference logged in the console, any help is appreciated
function CoinData(props) {
//fetch whichever coin we want to add
const NEWCOIN_QUERY = gql`
query tokens($tokenAddress: Bytes!) {
tokens(where: { id: $tokenAddress }) {
derivedETH
totalLiquidity
}
}
`;
const { loading: ethLoading, data: ethPriceData } = useQuery(ETH_PRICE_QUERY);
const { loading: allLoading, data: allTokenData } = useQuery(QUERY_ALL_TOKENS);
const { loading: coinLoading, data: coindata } = useQuery(NEWCOIN_QUERY, {
variables: {
tokenAddress: props.parentState.newcoins!== '' ? props.parentState.newcoins.toString() : '0x6b175474e89094c44da98b954eedeac495271d0f',
},
});
const coinPriceInEth = coindata && coindata.tokens[0].derivedETH;
const coinTotalLiquidity = coindata && coindata.tokens[0].totalLiquidity;
const ethPriceInUSD = ethPriceData && ethPriceData.bundles[0].ethPrice;
console.log(props.parentState.newcoins)
return (
<div>
<div>
coin price:{" "}
{ethLoading || coinLoading
? "Loading token data..."
: "$" +
// parse responses as floats and fix to 2 decimals
(parseFloat(coinPriceInEth) * parseFloat(ethPriceInUSD)).toFixed(2)}
</div>
<div>
Coin total liquidity:{" "}
{coinLoading ? "Loading token data...": parseFloat(coinTotalLiquidity).toFixed(0)}
</div>
<div>
</div>
<div>
//////////////////////////////////////////----map function////////////////////////////
{allLoading ? "Loading token data...":
<div>
{allTokenData.map((token, index) => (
<p key={index}> {token.id} SYN: {token.symbol}</p>
))}
</div>
}
//////////////////////////////////////////----map function////////////////////////////
</div>
</div>
);
}
maybe allTokenData.tokens.map.
It is because allTokenData is an object.
const {tokens} = allTokenData
{tokens.map((token, index) => (
<p key={index}> {token.id} SYN: {token.symbol}</p>
))}
I have to do something like:
email ? do_this : icon ? do_that : do_something_else
This can be done very simple using nested ternary but this ESLint rule doesn't make this possible.
On their documentation they recommend to use if-else but I don't know how to implement this in my case
The code works fine with one ternary.
return (
<td>
{email ? (
<span>...</span>
) : (
<span>...</span>
)}
</td>
adding nested ternary would return that ESLint error and using if-else says that if is an unexpected token:
return (
<td>
{if(email) return ( <span>...</span>);
else if(icon) return ( <span>...</span>);
else return ( <span>...</span>);
}
</td>
Is it possible to solve this problem?
You can store the cell content in a variable:
let content;
if(email) {
content = <span>...</span>;
} else if(icon) {
content = <span>...</span>;
} else {
content = <span>...</span>;
}
return <td>{content}</td>;
I find it useful to extract a complex functionality for readability:
import React from 'react';
// extracted functionality
const emailAction = (email, icon) => {
if (email) {
return <span>Do This</span>;
} else {
if (icon) {
return <span>Do That</span>;
} else {
return <span>Do Something Else</span>;
}
}
};
// your Component
export const TableData = (props) => {
return <td>{emailAction(props.email, props.icon)}</td>;
};
Another option is to use something like an enum to render:
if (email) {
content = 'email';
else if (icon) {
content = 'icon';
} else {
content = 'other';
}
return (
<td>
{{
email: <span>...</span>,
icon: <span>...</span>,
other: <span>...</span>,
}[content]}
</td>);
This is explained in more detail here: https://reactpatterns.js.org/docs/conditional-rendering-with-enum/
I have a custom autocomplete, so when you type, it will display a list of suggestions based on the input value. In the list, I would like to bold the characters that are the same as the input value.
So if I have a list of suggestions: "alligator", "lima", "lime", and I typed "li", then the suggestions would look like this:
alligator
lima
lime
I have this simple map in my jsx file:
<ul>
{matches.map(function(match, idx){
let re = new RegExp(value, 'g');
let str = match.replace(re, '<b>'+ value +'</b>');
return <li key={idx}>{str}</li>
})}
</ul>
where value is the input value. It displays the list but in this string format
al<b>li</b>gator
<b>li</b>ma
<b>li</b>me
Not sure how to go about with React. I thought of using dangerouslyinnerhtml or something like that, but I think that's a last resort thing. I would like to avoid that if possible.
This my autocomplete component:
class Autocomplete extends Component{
constructor(props){
super(props);
this.state = {
value: '',
matches: [],
showMatches: false
}
}
searchListing(){
api.call {
that.setState({
showMatches: true,
matches: a
});
})
}
}
handleOnChangeInput(e){
let value = e.target.value;
this.setState({ value: value})
if(value !== ''){
this.searchListing(e);
}else{
// console.log("value", e.target.value);
this.setState({
showMatches: false,
matches: []
})
}
}
render(){
let matches = this.state.matches;
let value = this.state.value;
let matchesHtml;
if(this.state.showMatches){
matchesHtml = <ul>
{matches.map(function(match, idx){
let re = new RegExp(value, 'g');
let str = match.replace(re, '<b>'+ value +'</b>');
return <li key={idx} dangerouslySetInnerHTML={{__html: str}}></li>
})}
</ul>
}
return(
<div>
<input placeholder="type a name" onChange={this.handleOnChangeInput}/>
{matchesHtml}
</div>
);
}
}
Writing your own highlighting code could lead down a rabbit hole. In my answer, I assume only simple text (no HTML within the strings, no charset edge cases) and valid non-escaped RegExp pattern string.
Instead of building a new string, you could build a new array, in which you could put JSX.
A React component can also return an array of elements:
render() {
// No need to wrap list items in an extra element!
return [
// Don't forget the keys :)
<li key="A">First item</li>,
<li key="B">Second item</li>,
<li key="C">Third item</li>,
];
}
The logic behind
As a simple proof of concept, here's the logic we could use:
const defaultHighlight = s => <em>{s}</em>;
// Needed if the target includes ambiguous characters that are valid regex operators.
const escapeRegex = v => v.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
/**
* Case insensitive highlight which keeps the source casing.
* #param {string} source text
* #param {string} target to highlight within the source text
* #param {Function} callback to define how to highlight the text
* #returns {Array}
*/
const highlightWord = (source, target, callback) => {
const res = [];
if (!source) return res;
if (!target) return source;
const regex = new RegExp(escapeRegex(target), 'gi');
let lastOffset = 0;
// Uses replace callback, but not its return value
source.replace(regex, (val, offset) => {
// Push both the last part of the string, and the new part with the highlight
res.push(
source.substr(lastOffset, offset - lastOffset),
// Replace the string with JSX or anything.
(callback || defaultHighlight)(val)
);
lastOffset = offset + val.length;
});
// Push the last non-highlighted string
res.push(source.substr(lastOffset));
return res;
};
/**
* React component that wraps our `highlightWord` util.
*/
const Highlight = ({ source, target, children }) =>
highlightWord(source, target, children);
const TEXT = 'This is a test.';
const Example = () => (
<div>
<div>Nothing: "<Highlight />"</div>
<div>No target: "<Highlight source={TEXT} />"</div>
<div>Default 'test': "<Highlight source={TEXT} target="test" />"</div>
<div>Multiple custom with 't':
"<Highlight source={TEXT} target="t">
{s => <span className="highlight">{s}</span>}
</Highlight>"
</div>
<div>Ambiguous target '.':
"<Highlight source={TEXT} target=".">
{s => <span className="highlight">{s}</span>}
</Highlight>"
</div>
</div>
);
// Render it
ReactDOM.render(
<Example />,
document.getElementById("react")
);
.highlight {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
No need to use dangerouslySetInnerHTML here.
This highlightWord function can take any function to wrap the matched string.
highlight(match, value) // default to `s => <em>{s}</em>`
// or
highlight(match, value, s => <span className="highlight">{s}</span>);
I'm doing minimal regex string escaping based on another answer on Stack Overflow.
The Highlight component
As shown, we can create a component so it's "more react"!
/**
* React component that wraps our `highlightWord` util.
*/
const Highlight = ({ source, target, children }) =>
highlightWord(source, target, children);
Highlight.propTypes = {
source: PropTypes.string,
target: PropTypes.string,
children: PropTypes.func,
};
Highlight.defaultProps = {
source: null,
target: null,
children: null,
};
export default Highlight;
It uses a render prop, so you'd have to change your rendering to:
<ul>
{matches.map((match, idx) => (
<li key={idx}>
<Highlight source={match} target={value}>
{s => <strong>{s}</strong>}
</Highlight>
</li>
))}
</ul>
Just use dangerouslySetInnerHTML but take attention it make to inadvertently expose your users to a cross-site scripting (XSS) attack
...
const valueToBold = (match: string) => {
const regex = new RegExp(searchFilterValue, 'g');
return match.replace(regex, '<b>$&</b>');
};
return (
...
<ul>
{matches.map((match, idx)=> (
<li key={idx}>
<span dangerouslySetInnerHTML={{ __html:valueToBold(match) }} />
</li>
))}
</ul>
...
)
You just append your mapper as children inside your auto complete component.
<CustomAutocomplete>
<ul>
{
matches.map(function(match, idx){
let re = new RegExp(value, 'g');
let str = match.replace(re, '<b>'+ value +'</b>');
return (<li key={idx}>{str}</li>)
})
}
</ul>
</CustomAutocomplete>
I have presentational component in React. And with products.some i am trying to check if any item inside products is checked. And if some item is checked, render parent block for RequestedProduct component. I know that the problem is a second pair of curly braces as React think it's a prop. Is there another way to do this?
const Requested = ({ products, getCurrentTime }) => (
<div className="pepper-pin-body-tab requested-tab">
<div className="pepper-pin-body-tab-title">
Запрошенные
</div>
<div className="pepper-pin-body-tab-indicator" />
{products.some(product => product.checked) ? (
<div className="requested-tab-list-requested">
<div className="requested-tab-list-requested-time">
{getCurrentTime()}
</div>
{products.filter((product, key) => {
if (product.checked) {
return (
<RequestedProduct
key={key}
title={product.title}
/>
);
}
})}
</div>
) : null}
</div>
);
Issue is, filter will not return the custom element/value, it will always return the array element for which you return true from filter body.
Solution is, use only map or combination of filter and map.
Using map:
{
products.map((product, key) => product.checked ?
<RequestedProduct key={key} title={product.title} />
: null
}
Using combination of filter and map:
{
products
.filter(product => product.checked)
.map((product, key) => <RequestedProduct key={key} title={product.title}/>)
}
Check this snippet, you will get a better idea:
const arr = [
{a: 1},
{a: 2},
{a: 3},
{a: 4}
];
const afterFilter = arr.filter((el,i) => {
if(i%2) {
return `Hello world ${i}`;
}
});
// it will print the array items, not the Hello World string
console.log('afterFilter', afterFilter);
I'd recomment splitting the code a bit, which makes it intent a lot clearer. You'll end up with the following (for example), which should not be triggering errors.
The main problem is in the unintended side effects of the filter, whereas you most likely want to use a filter and a map. That makes the intent to another developer much clearer.
const contents = (products, getCurrentTime) => (
const filtered = products.filter(product => product.checked);
<div className="requested-tab-list-requested">
<div className="requested-tab-list-requested-time">
{getCurrentTime()}
</div>
{filtered.map((product, key) => <RequestedProduct key={key} title={product.title}/>)}
</div>
);
const Requested = ({products, getCurrentTime}) => {
const isAnythingChecked = products.some(product => product.checked);
return <div className="pepper-pin-body-tab requested-tab">
<div className="pepper-pin-body-tab-title">
Запрошенные
</div>
<div className="pepper-pin-body-tab-indicator"/>
{isAnythingChecked ? contents(products, getCurrentTime) : null}
</div>
};