Dynamically determine Component to render at runtime - reactjs

I have the following code in a ReactComponent class:
render() {
const snippets = entity.snippets.map(
function (snippet, props) {
const SnippetType = snippet['type'];
//const SnippetType = "Text";
return <SnippetType key={Math.random()} />;
}
);
// .......
return (
<article>
{snippets};
</article>
)
}
The Text Component looks like this.
const Text = (props) => (
<div className="snippet text-snippet">
<h2>{props.name}</h2>
<p>{props.content}</p>
</div>
);
I'm not sure, what I'm doing wrong here, but <SnippetName ...> always renders HTML like instead of the actual Component, even if I define Text manually as a string (as per the commented line above).
After several hours of failing, I ask you: What am I doing wrong?

What are you storing in the snippet variable? If you store the results of loading a component (ie const snippet['type'] = require('MyTextComponent')), then this should work.
Rendering a string as a component doesn't work because a string isn't a component. Compare what a variable looks like when you set it to a component vs a string and you'll see what I mean (ie for your commented out //const SnippetType = "Text";.
Maybe try something like
const SnippetComponents = {};
SnippetComponents['text'] = require('MyTextComponent');
then you can go
const Snippet = SnippetComponents['text']
<Snippet someProps={stuff}/>

Related

How to access onCopy event value in React

I want to edit copied content in onCopy event. But I checked that the parameter of onCopy event is a clipboardevent. So, is there a way that i can edit content on Clipboard through clipboardevent vairiable. Codes are like
<div onCopy = {(e)=>{//edit content through variable e}}>
//something here
</div>
So, if you want to see the value that you are copying, just edit the code like this.
<div onCopy = onCopy={e => console.log(e.target.innerText)}>
the text is placed inside the "innerText" key.
For React just initialize a state using useState, where you can save the value.
const Test = () => {
const [data, setData] = useState(null)
return (
<div onCopy={e => setData(e.target.innerText)}>
Hello
</div>
)
}
For a single <div> with no child elements, you can access the copied content using:
e.target.innerText
OR
e.target.textContent
Such as:
<div onCopy={(e) => console.log(e.target.innerText)}></div>
// You can use the value like this:
function MyComponent() {
const [copiedValue, setCopiedValue] = useState("");
return (
<>
<div>{copiedValue}</div>
<div onCopy={(e) => setCopiedValue(e.target.innerText)}></div>
</>
)
}
NOTE: There are important differences between innerText and textContent if your <div> has any child nodes. One big difference is:
textContent gets the content of all elements, including <script> and <style> elements. In contrast, innerText only shows “human-readable” elements.
Source MDN Doc
I'm not getting the value of what I selected, I'm getting the entire value of the element!
Using the above event target properties, you will only be able to access the entire contents of the element being copied. If you want to copy the selected text from the user, you could do something like this:
function MyComponent() {
const [copiedValue, setCopiedValue] = useState("");
const handleOnCopy = (e) => {
const selectedText = document.getSelection(); // <-- this is different
setCopiedValue(selectedText);
};
return (
<>
<div>{copiedValue}</div>
<div onCopy={(e) => handleOnCopy(e)}></div>
</>
)
}
I'm getting undefined when accessing e.target.innerText and e.target.textContent!
This could be because you're trying to access the value of an <input> element. If you're trying to access a checkbox's boolean state use e.target.checked. For all the other <input>'s, use e.target.value

How to do useState in this case, I can't add component with props

I learn ReactJs and JavaScript and got stuck a bit
If I do like this:
const [data, setData] = useState([]);
const addData = val => {
setData([...data, val]);
};
It work as expected every time I run that addData the data will have one more val merging into it ok
But when I like this:
const addData = val => {
setData([...data, File: file={val}]);
};
And the File is this simple:
const File = (file) => {
const classes = useStyles();
// do stuff
return (
<Return something with props file />
);
};
export default File;
Then I get error I don't know how to give val to File before I add File to data
I tried like (File: File={val}) but no
The main issue is with File: file={val}. You have an array of objects so you should add a new object rather then a property. for that. you need to add this into a {}. Second, when creating a structure and asaigning values, use : not =. and 3rd - dont suround ur val in {}. Make it a simple val. If you wish to pass data to val, assuming it some sort of component, make it into an arrow function and load it as a component () => <Val />. NOTE, it has to start with a capital letter.
Here is a demo I made similar to what you want by my understandings.
import { useState } from "react";
export default function App() {
const [data, setData] = useState([]);
const File = ({ file }) => {
return <div>{file}</div>;
};
const clickMe = () => {
setData([...data, { File: () => <File file={"some file name"} /> }]);
};
return (
<div className="App">
<button onClick={clickMe}>Add</button>
{data && data.map(({ File }, i) => <File key={i} />)}
</div>
);
}
Whenever you click a button, a new component, called File will be added to state data and will pass a prop file into it. and in render all the data form data will be rendered.
NOTE You may use File: <File file={"some file name"} /> instead of using an arrow function File: () => <File file={"some file name"}. But this may create some issues and will load the component File when adding it to data instead of loading in render. hence an arrow function is a better and recomended way to go.
Live example: https://codesandbox.io/s/festive-sea-svhbs?file=/src/App.js:0-441

passing a function as a prop from a functional component to a class component

I am learning react by coding a gamertag generator. It generates random tags and keeps a list, and each can be rated. My main app is a functional component utilizing the useState hook to set state: an array of objects with details about the tags, namely a star rating system.
I generate each gamertag using a react component Tag, within, it uses a functional component, RenderStars to draw the stars. Each tag stars off with 0 stars, so 5 empty stars, and I want the user to change rating by clicking on however many stars, 1-5. RenderStars will then draw however many empty and filled stars as needed.
I have a function in App, changeStars, that I can't seem to get any of the child components to call successfully. I am passing the function to the child components through props.
I've tried writing changeStars in arrow notation and as a plain function. I've tried it without requiring any parameters. I've tried calling it within Tag just using a button. There's other ways I've messed with it that I can't quite recall, just messing with the syntax and trying other things from stackexchange and articles.
I don't bind the function because it's created in a functional component.
This seems like a super basic task and I can't figure it out. Yes i've read the Docs from react.js
Here is some of the code, I'll try to take out as much as possible:
function App() {
const [tagInventory, setTagInventory] = useState([]);
const latestTag = tagInventory[tagInventory.length - 1] ? tagInventory[tagInventory.length -1] : null;
const handleAdd = (tag) => {
uses hook to add tag to state
}
const makeTag = () => {
creates random tag
}
function changeStars(stars,key) {
console.log(stars, key);
//this will change the star rating of an individual tag
}
return (
<main>
A bunch of amazing interface html
<section className="tagInventory">
{tagInventory.map( (item) =>
<Tag
key={item.timeStamp}
tagItem={item}
/>
) }
</section>
</main>
);
};
class Tag extends React.Component {
render() {
const item = this.props.tagItem;
const stars = this.props.tagItem.stars;
const key = this.props.tagItem.timeStamp;
const tagClass = this.props.newTag ? "tag-item new-item" : "tag-item";
return (
<div className={tagClass}>
code to generate cool tag info
</div>
<RenderStars
stars={stars}
changeStars={changeStars}
newTag={false}
key={key}
/>
</div>
);
}
}
const RenderStars = (props) => {
// ref for using symbol tag https://css-tricks.com/svg-symbol-good-choice-icons/
return (
i load svg of stars then can display as many as i need later...
now i draw 4 stars, for the example i'll stop at the first, here's the call..
{props.stars === 0 &&
<div>
<svg className="empty-star" onClick={() => props.changeStars(4,props.key)}>
<use xlinkHref="#empty-star" />
now the other stars and whatnot
}
Thanks!
So basically you want to pass the function changeStars from App to Tag and then to RenderStars, is that correct?
If so, you're forgetting to pass it from App to Tag
App:
<section className="tagInventory">
{tagInventory.map( (item) =>
<Tag
key={item.timeStamp}
tagItem={item}
/>
) }
</section>
Should be passing the function:
<section className="tagInventory">
{tagInventory.map( (item) =>
<Tag
key={item.timeStamp}
tagItem={item}
changeStars={changeStars}
/>
) }
</section>
And then on Tag:
const changeStars = this.props.changeStars;

Understanding how to make React website mobile friendly

I've been playing around with react-responsive to understand how to make websites mobile-friendly. Essentially, what I want to do is pass in a value from a function that tests whether a viewing screen is sized like a mobile phone. Practically stolen from the documentation of react-responsive, I have a function in a file called Mobile.js as follows:
const Mobile = () => {
const mobile = useMediaQuery({ query: '(max-width: 1000px)' })
return (
<div>
{mobile && <p>You are sized like a mobile phone</p>}
</div>
);
}
However, what I want to be able to do is pass the boolean "mobile" into other classes in other js files where I can then use different CSS classNames depending on the value of this boolean.
I have 3 specific questions.
How would I return the boolean mobile from the Mobile function to be used?
How would I access this returned boolean?
How would I change the className of a div depending on the value of this boolean?
Pretty new to web development (especially React) and these questions seem super simple and easy to solve, but for some reason, I just can't seem to figure it out by my own online research. Would love direct help and also some resources where I could learn more. Thank you so much!
To me, in a perfect world, the right code would look like this in my mind. Not sure how far off I am, but I was hoping maybe this could be of some guidance as to how I'm thinking.
In the function file,
// FILE Mobile.js
const Mobile = () => {
const mobile = useMediaQuery({ query: '(max-width: 1000px)' })
return (
{ mobile } // how to return a value?
);
}
export default Mobile;
In another file,
// FILE OtherClass.js
import Mobile from './Mobile';
class OtherClass extends Component {
constructor(props) {
super(props);
this.state = { mobile: <Mobile /> } // how to access the returned value?
}
render() {
return (
<div>
{this.state.mobile && <div className="text-mobile">} // how to change the className depending on value?
{!this.state.mobile && <div className="text-desktop">}
blah blah blah blah blah
</div>
</div>
);
}
}
Thanks for asking and welcome to React development !
Here how I can help you
How would I return the boolean mobile from the Mobile function to be
used?
How would I access this returned boolean?
Since you are calling a hook useMediaQuery, you need also a hook to reuse it and returns its value :
function useIsMobile() {
const isMobile = useMediaQuery({ query: '(max-width: 1000px)' });
return isMobile
}
//Then you can share this logic in other components
function Component1(){
const isMobile = useIsMobile()
...
}
function Component2(){
const isMobile = useIsMobile()
...
}
Please note that you can't use hooks inside class components.
How would I change the className of a div depending on the value of
this boolean?
This is straightforward:
function Component(){
const isMobile = useIsMobile()
const className = isMobile ? 'mobile-class' : 'desktop-class'
return <div className={className}>...</div>
}
If you need more complex className logic you can checkout the package classnames which makes it very easy to activate/deactivate classes.
I might be misunderstanding but I think if I were to implement it according to the 3 questions you have it would be something like that:
const MyComponent = () => {
// the point of using hooks is to use it whenever you need it (you "hook it")
const isMobile = useMediaQuery({ query: '(max-width: 1000px)' });
const textStyle = isMobile ? 'text-mobile' : 'text-mobile';
return (
<div className={textStyle}>
enter code here
</div>
)
}
Hope this helps :)
Edit
To reuse this logic you can do a wrapper div component instead:
const MyWrapperComponent = (props) => {
const isMobile = useMediaQuery({ query: '(max-width: 1000px)' });
const textStyle = isMobile ? 'text-mobile' : 'text-mobile';
return (
<div className={textStyle}>
{props.children}
</div>
)
}
// Then you can use it like so:
const HomePage = () => (
<MyWrapperComponent>
write the rest of the code here
</MyWrapperComponent>
)
Children are a kind of props that are native to react, quite basically just means they are whatever you provide between the tags of the component that's receiving them :)
Hope this helps!

Replace a string with Component in ReactJS

I'm trying to replace a string with a React Component but it's not working. It's returning [object Object].
I've tried to use renderToString and renderToStaticMarkup from react-dom/server to render the component, but no success.
import React from 'react';
const MyComponent = ({ children }) => <strong>{children}</strong>;
function App() {
const content = 'Hi #user'.replace('user', <MyComponent>user</MyComponent>);
return <div className="App" dangerouslySetInnerHTML={{ __html: content }} />;
}
export default App;
Expected result:
Hi #<strong>user</strong>
Actual result:
Hi #[object Object]
To render components, they need to return those components as part of your rendering. This is typically done with jsx tags, which get transformed into calls to React.createElement, which in turn creates objects that instruct react what to do.
But with string.replace, you're only going to produce a string, not react elements, so react has nothing to work with other than that string. And as for dangerouslySetInnerHtml, that will only work if you have actual dom content you want to insert, not react components (plus, it's dangerous).
Most of the time when you're rendering components of an unknown quantity this is done by having an array of data, which you then map to the components. For example:
function App() {
const greetings = ['Hi #', 'Aloha #'];
const content = greetings.map(greeting => (
<React.Fragment>
{greeting}
<MyComponent>user</MyComponent>
</React.Fragment>
));
return <div>{content}</div>;
}
Taking in a string and trying to interrogate that string is rather unusual, but if that's what you need to do, then you'll probably want to do something like this:
function App() {
const str = 'Hi #userAloha #user';
const greetings = str.split('user');
greetings.pop() // remove an empty string from the end of the array
const content = greetings.map(greeting => (
<React.Fragment>
{greeting}
<MyComponent>user</MyComponent>
</React.Fragment>
));
return <div>{content}</div>
}
Note that this is basically identical to my first code, except there's an extra step to turn the string input into an array.

Resources