Clarification needed on React's JSX syntax - reactjs

I am learning to code in React and bit confused with JSX syntax.
I tried to understand but getting confused again and again.
It will be better if someone explains to me what exactly happening here with this code and what is the problem.
Here I am trying to iterate over form element array with below code:
const form = formElementArray.map(element =>{
<Input
key = {element.id}
elementType={element.config.elementType}
elementConfig={element.config.elementConfig}
value={element.config.value}
inValid = {!element.config.valid}
touched = {element.config.touched}
changed={(event)=>this.onChangeHandler(event,element.id)}
shouldValidate={element.config.validation}>
</Input>
})
Error: Expected an assignment or function call and instead saw an expression.

when using arrow function can emit the return keyword when you don't provide open/close brackets.
to fix remove { and } from your arrow function
const form = formElementArray.map(element =>
<Input
key = {element.id}
elementType={element.config.elementType}
elementConfig={element.config.elementConfig}
value={element.config.value}
inValid = {!element.config.valid}
touched = {element.config.touched}
changed={(event)=>this.onChangeHandler(event,element.id)}
shouldValidate={element.config.validation}>
</Input>
)

JSX expression:
{<div>some html tag with one root tags. wrapping brackets { and } </div>}
JSX means JavaScript XML. that says, you can write html in your JavaScript file.
Arrow function:
const randomFunc1 = (param) => {
var data = 'some data' + param;
return data;
}
OR
const randomFunc2 = param => {
var data = 'some data' + param;
return data;
}
OR
const randomFunc3 = param => 'some data' + param;
Above randomFunc1, randomFunc2, randomFunc3 doing same as one. randomFunc3 is the shortest syntax.
Your code is ok. but map function needs to return statement to create a new array. so just need a return keyword before <Input> tag

Related

ReactJS map: Expected an assignment or function call and instead saw an expression - no-unused-expressions

I'm in the midst of cleaning up errors for a repo and I've come across this error where someone's trying to to assign a tag value object to a const variable inside of a map function. Here's its current form:
const BatchEditState = {
CURRENT: 'CURRENT',
DELETE: 'DELETE',
PUT: 'PUT',
}
handleShow = () => {
this.batchEditSet = {};
this.state.currentTags.map((tag) => {
this.batchEditSet[tag.tag_name] = BatchEditState.CURRENT;
});
};
As far as I've researched, one is definitely not supposed to go about it this way even if it does still function. I've seen plenty examples returning a jsx element, but I'm pretty sure that's not the point for this. I do know a map function is supposed to at least return a value however.
I attempted to use a spread operator and an implicit return, but that didn't work out. I also tried making a basic return & even though I'm not encountering any immediate errors in our application, I'm still not sure if this is the right way to go. Still fairly new at this, but appreciate any info, help, and education I can get
handleShow = () => {
this.batchEditSet = {};
this.state.currentTags.map((tag) => {
this.batchEditSet[tag.tag_name] = BatchEditState.CURRENT;
return(
BatchEditState.CURRENT
)
});
};
.map is only for creating new arrays by iterating over an existing array. While you want to iterate over an existing array, you don't want to create a new array - rather, you want to construct a plain object - so .map should not be used here. (The array you're constructing in your current code is going unused.)
To procedurally assign to properties of the object, do:
handleShow = () => {
this.batchEditSet = {};
this.state.currentTags.forEach((tag) => {
this.batchEditSet[tag.tag_name] = BatchEditState.CURRENT;
});
};
Or create an array of entries, then turn that array into an object.
handleShow = () => {
this.batchEditSet = Object.fromEntries(
this.state.currentTags.map(tag => [tag.tag_name, BatchEditState.CURRENT])
);
};
But also, doing this.batchEditSet = in the first place looks like a mistake in React. If this is a component, you should almost certainly be calling this.setState instead of mutating the instance.

ReactJs: How to replace html and string template with a component?

I want to manage the content of the page from a content editor where I am getting page content from the API.
Check this screenshot.
I used two different react modules for this react-html-parser and react-string-replace but it is still not working.
Here is my code.
let pageData = '';
pageData = ReactHtmlParser(page.content);
// replacing contact us form with a contact us form component
pageData = reactStringReplace(pageData, '{CONTACT_US_FORM}', (match, i) => (
<ContactUsForm />
));
return <div>{pageData}</div>;
react-html-parser -> It is used to parse HTML tags which are in string format into tree of elements.
react-string-replace -> It is used to replace a string into react a component.
Note: If I use react-html-parser or react-string-replace individually then it works fine but it does not work together.
Any suggestion?
Depends on the expected structure of page.content. If it contains HTML you are right in using react-html-parser, which has a replace option.
import parse from 'html-react-parser';
const macro = '{CONTACT_US_FORM}';
const replaceContactUsForm = (domhandlerNode) => {
if (domhandlerNode.type === 'text' && domhandlerNode.data.includes(macro))
return <ContactUsForm />;
};
// ...
const elements = parse(page.content, { replace: replaceContactUsForm });
return <div>{elements}</div>;
Additionally, If the string {CONTACT_US_FORM} is embedded in text you could use react-string-replace to keep the rest of the text intact:
const replaceContactUsForm = (domhandlerNode) => {
if (domhandlerNode.type === 'text' && domhandlerNode.data.includes(macro))
return <>{reactStringReplace(domhandlerNode.data, macro, () => (<ContactUsForm />))}</>;
};
If page.content does not contain HTML you do not need react-html-parser. But judging from your screenshot some markup is probably contained.

How to resolve "serializes to the same string" message with Jest?

In my React app, I've built a function that accepts a string full of regular text and any number of URLs. It then converts these into a <span> in React with every URL inside of an <a href tag. The code works really well but I can't seem to write a Jest test for it.
Here's what I've tried so far:
expect(convertHyperlinks('http://stackoverflow.com'))
.toStrictEqual(<span><a href='http://stackoverflow.com' target='_blank'>stackoverflow.com</a></span>);
And:
expect(convertHyperlinks('http://stackoverflow.com'))
.toMatchInlineSnapshot(<span><a href='http://stackoverflow.com' target='_blank'>stackoverflow.com</a></span>);
In the former case I'm getting the "serializes to the same string" message.
In the latter case, it's showing me this:
Expected properties: <span>stackoverflow.com</span>
Received value: <span>stackoverflow.com</span>
Might anyone know how to build a passing test for this?
Robert
Update: Here's the code for the function in question:
export const convertHyperlinks = (text: string): React.Node => {
// Find all http instances
const regex = /http\S*/g;
const hyperlinkInstances = text.match(regex);
if (!hyperlinkInstances) {
return <span>{text}</span>;
}
// Break up `text` into its logical chunks of strings and hyperlinks
let items = [];
let idx1 = 0;
let idx2 = -1;
hyperlinkInstances.forEach((hyperlink) => {
idx2 = text.indexOf(hyperlink, idx1);
if (idx2 === idx1) {
items.push(hyperlink);
idx1 += hyperlink.length;
} else {
items.push(text.substring(idx1, idx2));
items.push(hyperlink);
idx1 = idx2 + hyperlink.length;
}
});
if (idx1 < text.length) {
items.push(text.substring(idx1, text.length));
}
return (
<span>
{items.map((item) => {
if (item.includes('http://')) {
const plainLink = item.replace('http://', '');
return (
<a href={item.toLowerCase()} target='_blank' key={plainLink}>
{plainLink}
</a>
);
} else {
return item;
}
})}
</span>
);
};
You are returning a ReactNode from the method, which is an object. But you are trying to assert as just a string. It would'nt work.
This is what you may be getting back from the method,
And so, you must assert against the object you got, and not the way you are doing it right now,
const result = convertHyperlinks('http://stackoverflow.com')
expect(result.props[0].key).equals('stackoverflow.com');
// similar kind of assertions.
Additionally, I would suggest you go the component route and just render the component in the test method and assert for presence of elements as opposed to diving into react objects.
A representation of the same is as follows,
Here is your component,
const ConvertToHyperlinks = ({text}: {text: string}) => {
// your logic and then returning DOM elements.
return <></>;
}
Then you use it anywhere as,
<div>
<ConvertToHyperlinks text={'https://www.test.com/'} />
</div>
In your unit test you can then,
const renderedComponent = render(<ConvertToHyperlinks text={''https://www.anytyhing.com}/>);
expect(renderdComponent.getByText('anytyhing.com')).ToBeInTheDocument();
Here I am using some Rect Testing Library method but the idea is same even if you use enzyme etc.

How to parse local scope variable in Typescript?

I am trying to parse string to object array in my React application.
const {newsEntity} = props;
const contentInString = {newsEntity.content}; <== not working
// const contents = JSON.parse(contentInString); <== hoping to use this someday
I got the following error ESLint: Parsing error: ',' expected.
I tried to removed the curly braces but it gives undefined
Content :
[{"content":"Umi Kalsum berharap kondisinya tetap baik","type":"text"},{"content":"Dream - Setelah menempuh perjalanan darat cukup panjang untuk menemui wanita diduga telah menghina keluarganya, Umi Kalsum dan Ayah Rozak akhirnya kembali rumahnya di Depok, Jawa Barat. Perjalanan panjang itu ternyata menguras tenaga orang tua Ayu Ting Ting tersebut.","type":"text"}
The content of {newsEntity} I notice only visible during the rendering
return (<div> {newsEntity.content}</div> );
const contentInString = {newsEntity.content}; this leads to syntax error.
you should extract this way const contentInString = newsEntity?.content
If you want to assign new object use:
const contentInString = {content: newsEntity.content};
If you want to get content from newsEntity use:
const contentInString = newsEntity.content;
Regarding the last part of the question - that's TypeScript error and it gives you the hint that something went wrong with your types.
You can or
create new variable and type will be inferred automatically
Or fix the type manually
//Checkout this hopefully this will help you
let GblVar=100;
function Fn() { //function
console.log(GblVar)
if (true) { //code block
console.log(GblVar)
}
function nested() { //nested function within someFn1
console.log(GblVar)
}
}
for (let c = 0; c < 3; c++){ //code block
console.log(GblVar);
}
function OthrFn1() { //another function
console.log(GblVar)
}
console.log(GblVar)

Cannot change text value of input element

I'm learning React so sorry if the question is silly. Anyway, I'm trying to change the text of an Input element if the filtered variable is null, so I did:
const contactContext = useContext(ContactContext);
const text = useRef<HTMLInputElement>(null);
const { filterContacts, clearFilter, filtered } = contactContext;
useEffect(() => {
if (filtered === null) {
text.current?.value = '';
}
});
but on this line: text.current?.value = '';
I get:
The left-hand side of an assignment expression may not be an optional property access.
what I did wrong?
UPDATE
return (
<form>
<input
ref={text}
type="text"
placeholder="Filter Contacts..."
onChange={onChange} />
</form>
)
Error message is pretty clear about what's wrong in your code.
Optional-chaining is not valid on the left-hand side of the assignment statement and you get this error because of the following statement in the useEffect hook
text.current?.value = '';
You can only use optional-chaining on the right-hand side of an assignment.
You can replace your optional-chaining code to an if statement
if (text.current) {
text.current.value = '';
}

Resources