This question already has answers here:
Usage of the backtick character (`) in JavaScript
(11 answers)
Closed 2 years ago.
I am new to JSX, and javascript in general, and I am confused by {} having multiple meanings based on what is in and around them. I am confused about the difference between {} and ${} in the following example from the React docs.
In an example explaining hooks, we see <button onClick={() => this.setState({ count: this.state.count + 1 })}>
Here, the innermost curly braces indicate that a JS object with one key: value pair is what is being passed as the sole parameter to this.setState().
Later, in the componentDidUpdate and componentDidMount methods we see document.title = `You clicked ${this.state.count} times/`;
In this snippet, the curly braces do not surround a key: value pair so I know it not a JSON object. It's inside {} in JSX so this.state.count should just evaluate to a number but what is the meaning of the $ in this expression?
Thanks in advance!
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
componentDidMount() {
document.title = `You clicked ${this.state.count} times`;
}
componentDidUpdate() {
document.title = `You clicked ${this.state.count} times`;
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
);
}
}
The `` and $ combination is a way to concatenate strings that's easier to read than the old way of using +.
For example say you have obtained the users firstname through some input you might output some string like
Hi There Mark!
where Mark is the users name.
You can do this with either
"Hi There" + user.firstname + "!"
or
`Hi There ${user.firstname}!`.
In JavaScript content between backticks is a template string and ${...} is the syntax you can use to embed a JavaScript expression inside the template.
In JSX {...} is used to assign a dynamic value (as opposed to a static one) to a prop on a component.
Related
I want to display my custom Tire element or simple div (depending on media queries) with different values on each call. But the ternary operator doesn't work (always displays Tire element, never div) and I'm getting "undefined" instead of string "1" that should be it's content.
Would anyone be so kind as to explain where I went wrong?
const Content = () => {
const isDesktop = window.matchMedia('(min-width: 110em)')
const displayTire = (props) => (isDesktop.matches
? <Tire className={`${props.title}`}>{`${props.content}`}</Tire>
: <div className={`${props.title}`}>{`${props.content}`}</div>)
displayTire.propTypes = {
title: PropTypes.string.isRequired,
content: PropTypes.string.isRequired
}
return (
{displayTire('pneu1', '1')}
)
export default Content
The first argument you pass is 'pneu1'.
This is assigned to the props variable.
You then read props.title.
Strings don't have title properties so the value is undefined.
Your prop types says that the first argument should be an object which contains two properties (title and content).
Pass an object that matches the definition instead of two plain strings.
You're not actually providing a title and content to the props of displayTire.
To do that, you should have done:
return (
{displayTire({ title: 'pneu1', content: '1'})}
)
I am working with react app in typescript. From API, I have this input:
a) list of variable names ["name", "surname"]
b) few strings in form of simple html with variables "<p>Hello, how are you {name}?</p>"
c) number of inputs with variables such as {input1: "name"}
everything as a string/JSON
what i need to do is: render simple html (only few tags) received from API but "create" binding between those dynamic inputs and variables in strings
in static world, result would look like:
[name, setName] = useState("")
<p>Hello, how are you {name}?</p>
<input type="text" onChange={e => setName(e.target.value)}/>
However, all of this is dynamic. String "<p>Hello, how are you {name}?</p>" doesnt get binded to the input on its own.
I tried:
setting variable [vars, setVars] = useState({}), property for each dynamic variable, with
a) dangerouslySetInnerHTML - renders only html (cannot bind the variable inside to the input)
b) react-html-parser - same as above
c) babel.transform - couldnt make it work as this is done dynamically and in browser, it cannot find the right preset, i couldnt make the mimified babel.js work with typescript How to render a string with JSX in React
do you see some easy way? For example how could i use React.createElement to render html with "live" variable inside, represented as {variableName}? Or maybe something out of the box? giving each elemnt a class and finding the class in DOM and editing the text with input change would be probably very non-optimal?
I hope this could be a better example:
{response:
{
variables: ["name", "name2", "mood"],
texts: [
"<em> Hello! My name is {name}</em>",
"<p> Hi {name} ! I am <strong>{name2}</strong> and I feel {mood} today</p>"
],
inputs: [
{
label: "How do i feel?"
input: {mood}
}
]
}
}
EDIT:
This should give you a good idea:
https://stackblitz.com/edit/react-ts-cwm9ay?file=DynamicComponent.tsx
EDIT #2:
I'm pretty good with React and interpolation, it's still in progress (specifically the docs, but the readme is complete) but I'm going to shamelessly plug my ReactAST library
EDIT #3 - If you're interested in doing crazy dynamic interpolation, then you might also want to check out a neat dynamic interpolation (and it's reverse) library
Let's assume this:
{
vars: ['name','surname'],
strings: ["<p>Hello, how are you {name}?</p>","<p> It's night to meet you {name} {surname}"],
inputs: {
input1: 'name',
input2: 'surname'
}
}
Create a component (or set of components) that can do this.
I haven't even ran any of this, but here's the idea. I'll put it in a stackblitz in a bit and polish it up to make sure it works:
const MyDynamicComponent = ({vars,strings,inputs}) => {
const [state,setState] = useState(vars.reduce((acc,var) => ({...acc,[var]:undefined}));
return (
<>
<MyDynamicText vars={vars} strings={strings} state={state}/>
<MyDynamicInputs onChange={(name,val) => setState({[name]:val}) state={state} inputs={inputs}/>
</>
)
}
const MyDynamicText = ({vars,strings,state}) => {
return strings.map((s,i) => s.replaceAll(`{${vars[i]}}`,state[vars[i]])
}
const MyDynamicInputs = ({onChange,inputs,state}) => {
return Object.entries(inputs).map(([inputName,varName]) => <input key={inputName} onChange={e => onChange(varName,e.target.value)} value={state[varName]}/>
}
I am creating a button for each string an an array (ie. majorDivisions = ["upper", "lower"]). I need each button to have an onClick function with parameters equal to the unique value (ie. "upper" or "lower") depending on which value was used to create the button. It is creating the correct buttons but for when calling the function, the parameter is not the string but instead some class object.
return(
<div>
{this.state.majorDivisions.map((division) => {
return <button onClick = {this.renderDivisionRequirements.bind(null, {division})}>{division}</button>
})}
</div>)
My code is successfully creating 2 buttons [lower] and [upper], however the onClick parameter {division} is appearing as a class object.
When you wrap your argument in curly braces like {division} , you are passing in an object. ie: { upper: "upper" } and { lower: "lower" }
This is recognized as a short-hand method to create an object inside a function declaration.
Just remove the curly-braces and the individual division parameter will be passed as expected to each button.
return(
<div>
{this.state.majorDivisions.map((division) => {
return <button onClick = {this.renderDivisionRequirements.bind(null, division)}>{division}</button>
})}
</div>)
Also, to make things a bit cleaner, I would recommend turning renderDivisionRequirements() into an arrow-function so you don't have to bind this. Or using anonymous function to call it on button-click.
renderDivisionRequirements = () => {
...renderDivisionRequirements logic
}
So your return logic can just be
return(
<div>
{this.state.majorDivisions.map((division) => {
return <button onClick = {() => this.renderDivisionRequirements(division)}>{division}</button>
})}
</div>)
Hi I am running the following , I have set textfield property to a string and as far as I know thats an array and map functions should work on arrays but it still says Cannot read property 'map' of undefined, well Is it not defined in there in the state? Thanks
class App extends Component {
state={
textfield:"first value",
}
makeUnique =(textfield)=>{
return String.prototype.concat(...new Set(textfield) );
}
textchanged = (event)=>{
this.setState({
textfield:event.target.value,
caltexlength:event.target.value.length,
})
}
render() {
let uniquechars=null;
uniquechars=(<div>
{
this.textfield.map((char,index)=>{
return <charComponent
char ={this.makeUnique(this.state.textfield)}/>
})
}
</div>)## Heading ##
Write the line this.textfield.map((char,index) as below.
[...this.state.textfield].map((char,index)
With the spread operator you will create an array from your string and you can call map on it.
TextField is a string so why are you doing map on it. Map works only on arrays. To access state TextField it should be this.state.textField but not this.textField
So change
uniquechars=(<div>
{
this.textfield.map((char,index)=>{
return <charComponent
char ={this.makeUnique(this.state.textfield)}/>
})
}
</div>)
To
uniquechars=(<div>
<charComponent
char ={this.makeUnique(this.state.textfield)}/>
</div>)
You have to replace
this.textfield.map
to
this.state.textfield.split('').map
I'm working on creating a reusable UI component and am trying to figure out how to allow the consumer of the component to provide their own template for a particular area of the component.
I'm using typescript and am trying to utilize string interpolation to accomplish this as it seemed the most appropriate course of action.
Here is what I have so far:
export class Pager {
pageNumber: number = 1;
getButtonHtml(buttonContentTemplate?: string, isDisabled?: boolean): string {
buttonContentTemlpate = buttonContentTemplate || '${this.pageNumber}';
isDisabled = isDisabled || false;
return `<button id="button-id" type="button" ${!isDisabled ? '' : disabledAttribute}>
${buttonContentTemplate}
</button>`;
}
}
I have some other methods that will update the page number based off user input/interaction, but I want it to work that when getButtonHtml gets called, the return value would be <button id="button-id" type="button">1</button>, but instead I'm getting <button id="button-id" type="button">${this.pageNumber}</button>.
Is there a way to get javascript to evaluate the string again, and interpolate the remaining place holders?
I've looked at the MDN article on this topic and think that the String.raw method might possibly be what I need to use, but I wasn't sure and no matter what I try, I haven't gotten it to work.
Any help would be greatly appreciated.
The problem is that Template literals are interpreted immediately.
What you want to do is lazy load the template. So it would be best to pass in a function that returns a string.
export class Pager {
pageNumber: number = 1;
getButtonHtml(template?: () => string, isDisabled=false): string {
template = template || function() { return this.pageNumber.toString() };
return `<button id="button-id" type="button" ${!isDisabled ? '' : disabledAttribute}>
${template()}
</button>`;
}
}
Additionally, you can take advantage of default parameters to avoid the || trick.