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}
          }
       ]
   }
} 
Dan
  • 109
  • 4
  • 13
  • Seems like a fun problem - could you define your input a bit better - could you post the JSON as it looks like when it's returned from the API? – Adam Jenkins Nov 05 '20 at 00:00
  • Would something like `"

    Hello, how are you {name}?

    ".replaceAll("{name}", "Dan")` do?
    – Tur1ng Nov 05 '20 at 00:04
  • @Tur1ng the problem is that the name should be binded to changes from the input, so if the input marked as {name} changes, the {name} from label string should too. At the moment i dont see how the changes are binded together in this way – Dan Nov 05 '20 at 00:13
  • 1
    @Dan that shouldn't be hard to do with the way React works. I guess Adam is working on a solution for you that uses `String.replaceAll`, I'll let him fill in the details! – Tur1ng Nov 05 '20 at 00:17
  • @Dan - what's the deal with `input:{mood}` in your updated question? How are you supposed to deal with that? – Adam Jenkins Nov 05 '20 at 00:24
  • 1
    @Adam sorry i just accidentally changed the way i marked it before, should have been just input: "mood" – Dan Nov 05 '20 at 00:31

1 Answers1

1

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]}/>
}
Adam Jenkins
  • 51,445
  • 11
  • 72
  • 100
  • Thank you, took me a while to understand some functional concepts but this is exactly what i have been looking for – Dan Nov 05 '20 at 00:39
  • 1
    @Dan - see my updated answer with a stackblitz here: https://stackblitz.com/edit/react-ts-cwm9ay?file=DynamicComponent.tsx – Adam Jenkins Nov 05 '20 at 00:40