Is there a way to return React Component as string instead of object?
Asked Answered
L

2

8

I want to introduce a multilingual system. I have a function component that returns a translation of a specific element based on selected language (from Context API).

Everything works well until I put the translated element into the form (option value, placeholder, etc.) - then it appears as an [object object].

That's where 2 questions are born:

  1. Is it possible to return this component as a something like string, which HTML forms would accept?

  2. Is it possible to apply context consumer to pure JS function, so it does not return a React component, but a primitive value?

Translation component:

const Translation = ({ element }) => {
  let translation;

  return (
    <LanguageConsumer>
      {({ language }) => {
        switch (language) {
          case "pl":
            translation = plTranslation;
            break;

          case "en":
            translation = enTranslation;
            break;

          default:
            translation = enTranslation;
        }

        return translation[element];
      }}
    </LanguageConsumer>
  );
};

Example:

<option value="en">
  <Translation element="globalLanguageEnglish" />
</option>

Thanks in advance for your help.

Limen answered 6/6, 2019 at 21:37 Comment(14)
Can you share some code? React can render a string object correctly. You are most likely trying to render the context object or something else.Dobla
Hard to help without code / context. However from what you're describing it sounds like you want to dynamically render a component based off its name. const ComponentToRender = 'MyComponent'; and using it would be like <ComponentToRender />. So if you have conditional elements to render you can pass just the components string name and then render itIntrogression
Sorry guys, I added some code. I guess the problem is that I am returning whole consumer, not only string element from translation object.Charlenacharlene
Or not. It always gives [object object] in form. Even if you do sth like const Value = () => "value" and pass it to input placeholder like <input type="text" placeholder={<Value />}. :-(Charlenacharlene
In that case you would just call Value as a function instead of rendering it as a component: <input type="text" placeholder={Value()} />Leekgreen
@BrianHadaway Right, and this is the crux of this problem. How can I use Context API with pure JS function and whether its possible at all.Charlenacharlene
can you post like a jsfiddle reproducing this issue? Its hard to understand what you're trying to do.Introgression
I get it. Have you tried wrapping the return in a Fragment? return (<React.Fragment>{translation[element]}</React.Fragment>);Leekgreen
@JohnRuddell Here you are: codesandbox.io/s/musing-ives-5kw9hCharlenacharlene
@BrianHadaway Wrapping with fragment doesn't work :(Charlenacharlene
Sorry was busy, just looked at it. Why not just pluck the object from your context instead of using a component to do this?Introgression
@Limen what I was describing is something more like this. Instead of holding yourself to a component.. you can just pass the data through and use what you want as vanilla JS. If this solves your issue in the way you want, please let me know and I'll write it up as a solution :)Introgression
@JohnRuddell Well, this solution is also satisfying for me and works cool. Thank you all for help :)Charlenacharlene
What happens if each component will have it's own map of language with different sets of translation.json files?Boehmenism
C
0

I can confirm it doesn't work - <option> html element content can be "Text, possibly with escaped characters (like &eacute;)."

React component can return only string, it's perfectly valid (and works as OP stated) ... but this use case is not supported.

Using components to just return a string is not a good choice. It can be ineffective when you need many translated strings in one component. React has to manage it as components, control props, render, make reconcilation etc. Many, following components (siblings) of the same type (only different props) should be key-ed.

Smarter solution: In this case pass an array of options (keys) as prop and use one component to render all translated <option> elements in a loop (return using fragment), f.e. like this:

// get some translation fn f.e. from context
return (
  <>
    {props.items.map( item => <option key="{item}" value="{item}">{translate(item)}</option> )}
  </>
)

Look how it's done at ready i18n solutions like i18next described here - pass function 't' and use it to access your translations.

Chabot answered 7/6, 2019 at 5:11 Comment(2)
What if I want to use React's useContext and manipute some value and then return it as a primitive string? Is there any way to achieve that?Boehmenism
Why would it be more ineffective to have a component that returns a string? Your answer does not give any valid argument. React has state management and in many cases that would actually be more effective if you re-processed the string only when the value changed.Leavings
L
0

I had a similar need where I had to support translated strings and wanted to return a "string" from a react component. I wanted to be able use the component with a typical function call and in the render section. Here is what I ended up doing.

This is the react component which returns the "string" value. Side note: I'm using redux to store a dictionary of strings but you can use something else.

import { useSelector } from 'react-redux';

export function StringValue({ stringId }) {
  const myStrings = useSelector((state) => state.strings);

  // Note: do your own error handling as required
  return myStrings[stringId];
}

Here is an example of how to use the component with a typical function call and as a component in render:

import { StringValue} from 'StringValue';
...

export function MyComponent(){
    ...

    return (
        <>
            {console.log('StringValue ', StringValue({ sId: 'myStringKey' }))}

            <StringValue sId="myStringKey" />
    ...
Lothair answered 6/2, 2023 at 17:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.