Display a React Component as a string - reactjs

I'm trying to display a helper string text with a react component. The string is part of an array of messages that get displayed on a screen. In this particular string, I'm using an icon for part of the string. The icon is a component. However, I keep getting back [object, object].
What I've tried...
`Click on the ${(<EyeIcon />)} to view the password you have entered.`,
Also just displaying the icon,
<EyeIcon />
I've even tried making the component into a export function.
EyeIcon()
But I can't seem to get it to appear.

Ok, there's some difference between a function, and a function component.
function
const eyeIcon = s => `Click on ${s}`
This is a function that you can plugin to output a new string based on s.
function component
const EyeIcon = ({ s }) => <span>Click on {s}</span>
This is a function component that you can use via
const Title = () => {
return <EyeIcon s="abc" />
}
A function component is a function, but it needs to be invoked to produce an element (not a string), therefore the return statement is not a string (Even you output a string there, it'll be wrapped into an element for you).
NOTE:
it's a bit confusing if it's your first time into functional programming, but just watch out for the input and output. The function component input argument is a props object, and it returns a DOM-like element (called React element). There's quite a bit ES6 syntax to confuse you further.

If this string is part of some conditions try this option:
<>Click on the (<EyeIcon />) to view the password you have entered.</>

Related

getPropertyValue function returns empty string on given DOM element in React TypeScript - How to read CSS vars in React

I've made a simple utility function that should read given CSS variable using getPropertyValue function on :root element.
Code:
export const getCssVariable = (name: string): string => {
const value = window
.getComputedStyle(document.documentElement)
.getPropertyValue(name);
return value;
};
This works if used in useEffect hook, but outside of it, it returns an empty string.
Why does this happen?
Is there another way to read CSS variables in React? Maybe through custom hook?
Thanks.
To access DOM elements, and by that also also their css property values, or computedStyle, your react code first needs to be mounted.
Writing code in useEffect(() => // your code here, []) ensures that the code is only run after your react code is mounted.

How to test react components props (expect component to be called with)

I need to test that a react component is called with opened={true} prop after an button click is fired. I am using testing-library ( testing-library/react + testing-library/jest-dom).
I mocked the Component using something like
import Component from "./path-to-file/component-name"
...
jest.mock("./path-to-file/component-name", () => {
return jest.fn().mockImplementation(() => {
return null
})
})
I first tried with:
expect(Component).toBeCalledWith(expect.objectContaining({"opened": true}))
expect(Component).toHaveBeenCalledWith(expect.objectContaining({"opened": true}))
expect(Component).toHaveBeenLastCalledWith(expect.objectContaining({"opened": true}))
but I got Error: expect(jest.fn()).toBeCalledWith(...expected).
Same went for expect.objectContaining({"opened": expect.anything()})
And even for expect(Component).toBeCalledWith(expect.anything())
And the difference is empty array:
I also tried with expect(ChartMenu.mock).toBeCalledWith(expect.anything()). I got a different error but still not working (this time the error was Error: expect(received).toBeCalledWith(...expected) + Matcher error: received value must be a mock or spy function)
Thank you in advice!
EDIT: here is a simplified version of the component I want to test:
const Component = () => {
const [chartMenuOpened, setChartMenuOpened] = useState(false)
return (
<Flex>
<EllipseIcon onClick={() => setChartMenuOpened(true)}>
+
</EllipseIcon>
<ChartMenu
opened={chartMenuOpened}
close={() => setChartMenuOpened(false)}
/>
</Flex>
)
}
Basically I want to make sure that when the + icon is clicked the menu will be opened (or called with open value). The issue is that I cannot render ChartMenu because it needs multiple props and redux state.
I was able in the end to mock useState in order to check that the setState was properly called from the icon component (in order to make sure there won't be future changes on the component that will break this using this post).
But I would still really appreciate an answer to the question: if there is any way to create a spy or something similar on a react component and check the props it was called with? Mostly because this was a rather simple example and I only have one state. But this might not always be the case. Or any good idea on how to properly test this kind if interaction would be really appeciated.
I think you are on the right track to test if the component has been called with that prop, it's probably the syntax issue in your code
I learn this trick from colleague and you can try to see if this helps fix your issue.
expect(Component).toHaveBeenCalledWith(
expect.objectContaining({
opened: true,
}),
expect.anything()
);
While the question on how to is answered, I did some extra research on this and it seems like in React components there is a second parameter refOrContext (so basically most of the time it's an empty object, but it can also be a ref or a context)
Despite pointing out the reason for the behavior, I also wanted to highlight that it is safer to use expect.anything() as the second argument (rather than just {} which would work only in most of the cases ):
More information about React second argument here

Can you explain this react native code (strange arrow function in render method)?

So this is some React Native code from a textbook that I'm going through, specifically it is from the render method of App.js. Of course the /* ...*/ would be filled in with actual code but it's irrelevant to my question.
<MeasureLayout>
{layout => (
<KeyboardState layout={layout}>
{keyboardInfo => /* … */}
</KeyboardState>
)}
</MeasureLayout>
What I don't understand is what is happening with {layout => (.... So I take it that layout is an arrow function that returns this keyboardState component. So how does layout then pass itself into keyboardState's layout prop at this part <KeyboardState layout={layout}>? And why would I want to do that exactly? This whole part here is really baffling me.
React components have props and children properties. The children property is usually a React node, but it can also be a function that returns a React node.
So how does layout then pass itself into keyboardState's layout prop at this part ?
The MeasureLayout component was created so that its children property was defined as a function instead of a React node.
And why would I want to do that exactly?
For dependency injection and as a pattern that allows for a more declarative style of programming with class-based components.
Some more in depth reading:
Topic: Functions as children
https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9
https://codedaily.io/tutorials/6/Using-Functions-as-Children-and-Render-Props-in-React-Components
See that {} inside render method is used for some javascript statements.
For eg.
<Text>
{personFirstNam +" " +personLastName}
</Text>
But now that in your code there is again JSX elements inside {}, it is used inside unnamed function.
i.e.
{layout => (
...// here you can use JSX element which will be returned into render method for UI.
)}
alternatively, if you want some operations there,
{layout =>{
let extractData = fromSomeWhere;
let calculatePosition = getPosition();
return (<KeyboardState layout={layout}>
{keyboardInfo => /* … */}
</KeyboardState>)
}}
All of these was to just do some JS statement executions/operations inside one JSX element.
The <MeasureLayout> is passing an argument to its children as a function. and to recieve it an arrow function is used.
so, basically the code of <MeasureLayout> will be,
function MesauseLayout(props){
//Do things
// layout = some result.
return <div>{props.children(layout)}</div>
}
So, in order to receive this the child will have to be inside a function that accepts this value. So, an arrow function is used to receive this value.
<MeasureLayout>
{layout => (
<KeyboardState layout={layout}>
{keyboardInfo => /* … */}
</KeyboardState>
)}
</MeasureLayout>
But in my opinion, using a Context/Provider with a hook will be a better option if that is possible. This is generally only used in extreme cases. There is also another option to use React.cloneElement and passing additional props. But there are tradeoffs if you have to choose between these two. Plus, There is a concept called render props which is commonly used in new libraries.

cannot render a complex React Element in order to replace a DOM element by id

I am a beginner with react so please excuse me if this question does not make sense.
I have a complex div element in the html document with an id, it looks like this:
aaavote: I am comingChange My voteDelete Me
and it was originally created from rendering a react element that was returned from a component, like this:
let content = visitors_list.map((visitor) =>
)
{content} was returned from the react component.
At some stage I need to replace that div (with id="MyDiv2") with a different content.
I have a function that creates this new "content" exactly the same way I created the original one, however, now instead of return it from the component and have react do the rendering, I need to do it manually and call:
document.getElementById("MyDiv2").innerHTML = something
I cannot just pass content as something because : component is an array of [object Object]
I cannot pass it as a json structure because JSON.stringify(content) gives me:
[{"type":"div","key":"1","ref":null,"props":{"className":"flex-no-shrink w-full md:w-1/4 md:px-3","children":{"key":null,"ref":null,"props":{"visitor":{"id":"1","name":"aaa","vote":0,"imageUrl":"http://www.stone-guitar-picks.com/stoneguitarpicksshop/images/large/GP2046_LRG.JPG"}},"_owner":null,"_store":{}}},"_owner":null,"_store":{}}]
so I tried: ReactDOM.render(content, document.getElementById("MyDiv2"))
but ReactDOM.render does nothing, infact it breaks and exists the function.
I also tried using React.createElement(content) - also did not work...
How can I solve this problem. the only solution that I found to work is forcing the refreshing of the page which I do not want to do.
Dont try to handle the dom manually, react does this for you!, imagine you have your component MyInput that renders an input with some characteristics that may be passed by properties as well, then you could just change your component props and react will change your component automatically. i'll try to post a hardcoded example:
const myComp = ({text, visible}) = return (
<input text={text} visibility={visible}>
)
then, when you change your component text it will render it automatically.

ref callback is being applied to first matching class

I have a simple component that has a render like this:
render: function(){
return (
<textarea className="wmd-input" id="wmd-input" ref={this.initiatePagedown}></textarea>
)
}
initiatePagedown: function(input){
//code that initiates markdown editor.
attr = $(input).attr('id').split('wmd-input')[1];
converter = new Markdown.Converter();
Markdown.Extra.init(converter, {highlighter: "highlight"});
editor = new Markdown.Editor(converter, attr);
return editor.run();
},
The component mounts n different times, hence creating multiple textareas.
The problem is the ref callback is running with the input of the first component, so it's always the first component that's manipulated, not the one that I select. So let's say this component was mounted twice, then the ref callback will be called on the first component instance twice, not once of each component instance. How do I solve this issue?
You're using the Pagedown editor to point to a specific element ID on the page, because you're passing in the second argument, attr:
new Markdown.Editor(converter, attr);
Check out the documentation for the second argument of the Markdown.Editor constructor:
If given, this argument is a string, appended to the HTML element ids of the three elements used by the editor...so you may create the second input box as <textarea id="wmd-input-2"> and pass the string "-2" as the second argument to the constructor.
Right now you're always creating an editor with the same ID:
id="wmd-input"
So the editor constructor will always match every existing instance of that ID on the page.
This is a very poor API forcing you to point to element IDs. As a workaround I would probably make the id a prop you pass in, so that the wrapping component/page can decide how many editors should be there, and you can build the ID like this
return (
<textarea id={ `wmd-input-${ this.props.id }` } ... />
)
Then you can instantiate the unfortunate API with something like
editor = new Markdown.Editor(converter, `-${ this.props.id }`);

Resources