Writing javascript in JSX with expression - reactjs

I am trying to understand the concept of writing expression in JSX but unable to understand that how is not javascript in curly braces an expression?
const Welcome()=>{
const isLoggedIn = true;
return(
<div>
{
if(isLoggedIn){
<p>Welcome!</p>
}else{
<p>Please Login</p>
}
}
</div>
);
}
Please guide me either when we assign a value isLoggedIn is true then validation happens, if value is true then it prints Welcome otherwise please log in.
Please tell me how is this a statement and not an expression.

If you want to use if then you have to use ternary operator because if in java scripts are statements, not expressions.
const Welcome = () => {
const isLoggedIn = true;
return (<div>{isLoggedIn ? <p>Welcome!</p> : <p>Please Login</p>}</div>);
}

if statements in JavaScript are, by definition, statements, and not expressions.
An expression can be considered as code that you can assign to a variable:
const myVar = 2 + 2;
const otherVar = someFuncCall();
Here, 2 + 2 and someFuncCall() are expressions because they can be assigned to a variable.
An if statement can't be assigned to a variable:
const invalidCode = if (someCondition) { "ABC"; } // This code won't run
You can use a ternary operator instead, which can be used to create expressions just like any other operators (e.g. + operator):
const Welcome = () => {
const isLoggedIn = true;
return(
<div>
{isLoggedIn ? (<p>Welcome!</p>) : (<p>Please Login</p>)}
</div>
);
}
This code works, because it's possible to assign this result to a variable:
const myJsx = isLoggedIn ? (<p>Welcome!</p>) : (<p>Please Login</p>)

Related

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.

Can someone explain me the difference between both this if conditions in React?

state = {
count: 1,
};
render() {
let classes = "badge m-5 bg-";
let { count } = this.state;
count === 1 ? (classes += "success") : (classes += "warning");//1st Condition
classes+= (count===1)?"success" : "warning";//2nd Condition
return (
<div className="container">
<span style={this.styles} className={classes} id="bad">
Hello!
</span>
</div>
);
}
I understood how that if condition works(condition ? true: false), but in the 2nd condition how it is possibly working even after placing the classes+= even before mentioning the condition?
Let's break it down. Remember, our initial value of count is 1.
let classes = "badge m-5 bg-";
...
classes+= (count===1)?"success" : "warning";
Firstly what'll happen is that count===1 will be checked for strict equality (it takes precedence), which will yield true. Since our condition is true, and we're using ternary operator, the expression returns "success" string, i.e.,
this:
classes+= (count===1)?"success" : "warning";
becomes this:
classes+= "success";
"success" will be appended to the classes string, therefore classes will become badge m-5 bg-success
In 2nd Condition, tt related to javascript operator precedence. ?: before +=
In Javascript, ternary operator (?...:) takes precedence over assignment (+=), so the right hand of the += resolves first and append either "success" or "warning" to classes.
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#table

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 = '';
}

Clarification needed on React's JSX syntax

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

Is there a better way to use conditionals inside jsx?

I've been learning React for the past few weeks and one thing that I don't like is that I have to use ternary operator for if else inside the render function.
Something like:
function render() {
return (
{x==="Let's go" ? <Go /> : <Hold on />}
)
}
Is there a way I can use a traditional if-else or switch for that matter with jsx syntax in React?
I utilize a few approaches to clean up the render method of more complex components.
1) Use a variable. Before you are actually in the JSX portion (the return) you can use the flexibility of raw JS to build variables. To take your example...
function render() {
let body;
if (x === "Let's go") {
body = <Go />
} else {
body = <Hold on />;
}
return (
<div>
{body}
</div>
);
}
Note that the top level needs to be wrapped, just put an extra div in there.
2) Use a function. This example is probably a little too simplistic but you'll get the idea..
renderBody() {
let body;
if (x === "Let's go") {
body = <Go />
} else {
body = <Hold on />;
}
return (
{body}
)
}
render() {
return (
<div>
{renderBody()}
</div>
)
}
3) Use an array (really a subset of #1) but oftentimes I find scenarios where sometimes I need to return 1 element but other times I need to return a variable number of elements. I will create an array at the top of the function and then push/unshift elements onto the array. Note that any time that you are building an array you must provide a key for each element so that React can update it properly.
let els = [
<div key="always">Always here</div>
];
if (foo) {
els.push(<div key="ifFoo">Conditionally Here</div>)
}
And then you just use the {els} variable in your main JSX.
4) Return null when you don't want anything to render
I prefer this syntax
function render() {
if (x==="Let's go") return <Go />;
return <Hold on />;
}

Resources