React variable component name - all lower case? - reactjs

I am trying to render icons that are set in the back end for each service on the page. The data comes from an api and includes the correct names for the icons. I am importing the needed icons from react-icons before hand...
I tried it this way:
//Services and icons
var serviceICON = this.state.services.map(service => {
let Icon = service.Icon
return <tr><td><Icon /></td><td> {service.serviceName}</td></tr>
});
This almost works - the only problem is that the icons are not rendered. Instead the html looks like this:
<fabed></fabed>
I don't understand why this happens. The api delivers the correct name (=> FaBed), so why is this rendering as all lower case?
Thanks a lot for your help in advance!
Edit:
The complete array for one of the services would look like this:
id: 2
serviceName: "Hotel"
trip: 6
Icon: "FaBed"
created_at: "2020-07-08T06:45:02.239Z"
updated_at: "2020-07-22T07:52:05.066Z"
I am mapping through each of these and try to output the code above. As you can see "Icon" comes with the correct spelling. So I don't understand why it is rendered in all lower case...

Basically what is happening here is Icon is just a string so the react framework treats it differently.
html tags vs react components
<Icon />
is converted to
React.createElement(Icon, {});
Choosing the Type at Runtime
You'll notice here the key is using a map to match a string value with an actual React Component. For your case you'll need to map service.Icon to the relevant (imported) Icon Component.
import FaBed from '...';
...
const icons = {
...
"FaBed": FaBed,
...
};
...
var serviceICON = this.state.services.map(service => {
let Icon = icons[service.Icon];
return <tr><td><Icon /></td><td> {service.serviceName}</td></tr>
});

Related

How to setup React Router <Link> inside a Datatables.net Cell?

I have a datatables table (https://datatables.net) initialized like this:
$('#example').DataTable({
data: data,
columns: [{
title: "Database ID",
data: 'database_id',
render: function (data, type, row) {
return '<NavLink to="/shop">'+ data +'</NavLink>';
}
}]
});
The NavLink that i have in the code is supposed to render a database cell as a clickable link because of React-Router (This whole function is inside a React component), but the link is not rendering when i run the code.
What i ultimately want is the ability to click on a database cell that will take me to another component by routing into a link like "/shop/id" but i am stuck in it for a long time. Help!
I was facing the same issue and below solution is working for me.
You can directly add anchor tag with href to the path where you want to route upon on the click. But, it will reload your application.
To avoid reloading of the application, try below solution.
When you're initializing the columns dynamically, you can add below code for the column where you want to have a link.
fnCreatedCell: (nTd, data) => ReactDOM.render(
<a
onClick={() => handletableclick(data, props)}>
{data}
</a>, nTd)
Add handletableclick() function/method in your file to handle click event and programmatically through history push the path where you want to route upon the click
function handletableclick(data, props) {
props.history.push("/pathToRoute=" + data);
}
Also,in above code to access history from props, you will need to enclose your component with withRouter
like below :
export default withRouter(YourComponentName);

snap.svg - embed svg in svg

I'm using snap-svg to display some SVG. I used this answer to do so in react.js.
However, I'd like to take it a step further: is it possible to load 2 svgs from urls, and then embed one inside of the other ?
So far, I managed to load my two SVGs, and then add them to my div, using this simple bit of code
const element = Snap(this.svgDiv)
Snap.load(url, function (baseProduct) {
if (element) {
element.add(baseProduct);
let designUrl = [my url]
Snap.load(designUrl, function (design) {
if (element) {
console.log(baseProduct);
baseProduct.select('rect').attr({fill: 'transparent'})
const image = design.select('image');
baseProduct.select('rect').add(image)
}
})
}
})
Unfortunately, this only displays the first image: the second one, which is supposed to be displayed inside of a rect,is not displayed. However, if I inspect my SVG, the img tac can be found inside of my rect. How comes so ?

Not allow to require by using variable? [duplicate]

I have read several posts about issues that people are having with React Native and the require() function when trying to require a dynamic resource such as:
Dynamic (fails):
urlName = "sampleData.json";
data = require('../' + urlName);
vs. Static (succeeds):
data = require('../sampleData.json');
I have read on some threads that this is a bug in React Native and in others that this is a feature.
Is there a new way to require a dynamic resource within a function?
Related Posts (all fairly old in React time):
Importing Text from local json file in React native
React Native - Dynamically List/Require Files In Directory
React Native - Image Require Module using Dynamic Names
React Native: how to use require(path) with dynamic urls?
As i have heard of, react's require() only uses static url not variables, that means that you have to do require('/path/file'), take a look at this issue on github and this one for more alternative solutions, there are a couple of other ways to do it!
for e.g
const images = {
profile: {
profile: require('./profile/profile.png'),
comments: require('./profile/comments.png'),
},
image1: require('./image1.jpg'),
image2: require('./image2.jpg'),
};
export default images;
then
import Images from './img/index';
render() {
<Image source={Images.profile.comments} />
}
from this answer
Here is my solution.
Setup
File structure:
app
|--src
|--assets
|--images
|--logos
|--small_kl_logo.png
|--small_a1_logo.png
|--small_kc_logo.png
|--small_nv_logo.png
|--small_other_logo.png
|--index.js
|--SearchableList.js
In index.js, I have this:
const images = {
logos: {
kl: require('./logos/small_kl_logo.png'),
a1: require('./logos/small_a1_logo.png'),
kc: require('./logos/small_kc_logo.png'),
nv: require('./logos/small_nv_logo.png'),
other: require('./logos/small_other_logo.png'),
}
};
export default images;
In my SearchableList.js component, I then imported the Images component like this:
import Images from './assets/images';
I then created a new function imageSelect in my component:
imageSelect = network => {
if (network === null) {
return Images.logos.other;
}
const networkArray = {
'KL': Images.logos.kl,
'A1': Images.logos.a1,
'KC': Images.logos.kc,
'NV': Images.logos.nv,
'Other': Images.logos.other,
};
return networkArray[network];
};
Then in my components render function I call this new imageSelect function to dynamically assign the desired Image based on the value in the this.state.network:
render() {
<Image source={this.imageSelect(this.state.network)} />
}
The value passed into the imageSelect function could be any dynamic string. I just chose to have it set in the state first and then passed in.
I hope this answer helps. :)
For anyone reading this that cannot work with the existing answers, I have an alternative.
First I'll explain my scenario. We have a mono repo with a number of packages (large react-native app). I want to dynamically import a bunch of locale files for i18n without having to keep a central registry in some magic file. There could be a number of teams working in the same monorepo and the DX we want is for package developers to be able to just add their local files in a known folder {{packageName}}/locales/en.json and have our core i18n functionality pick up their strings.
After several less than ideal solutions, I finally landed on https://github.com/kentcdodds/babel-plugin-preval as an ideal solution for us. This is how I did it:
const packageEnFiles = preval`
const fs = require('fs');
const path = require('path');
const paths = [];
const pathToPackages = path.join(__dirname, '../../../../packages/');
fs.readdirSync(pathToPackages)
.filter(name => fs.lstatSync(path.join(pathToPackages, name)).isDirectory())
.forEach(dir => {
if (fs.readdirSync(path.join(pathToPackages, dir)).find(name => name === 'locales')) {
const rawContents = fs.readFileSync(path.join(pathToPackages, dir, 'locales/en.json'), 'utf8');
paths.push({
name: dir,
contents: JSON.parse(rawContents),
});
}
});
module.exports = paths;
`;
Then I can just iterate over this list and add the local files to i18next:
packageEnFiles.forEach(file => {
i18n.addResourceBundle('en', file.name, file.contents);
});
If you need to switch between multiple locally stored images, you can also use this way:
var titleImg;
var textColor;
switch (this.props.data.title) {
case 'Футбол':
titleImg = require('../res/soccer.png');
textColor = '#76a963';
break;
case 'Баскетбол':
titleImg = require('../res/basketball.png');
textColor = '#d47b19';
break;
case 'Хоккей':
titleImg = require('../res/hockey.png');
textColor = '#3381d0';
break;
case 'Теннис':
titleImg = require('../res/tennis.png');
textColor = '#d6b031';
break;
}
In this snippet I change variables titleImg and textColor depending of the prop. I have put this snippet directly in render() method.
I have found that a dynamic path for require() works when it starts with a static string. For example require("./" + path) works, whereas require(path) doesn't.
Simple to dynamic images (using require)
Example array(into state)
this.state={
newimage: require('../../../src/assets/group/kids_room.png'),
randomImages=[
{
image:require('../../../src/assets/group/kids_room.png')
},
{
image:require('../../../src/assets/group/kids_room2.png')
}
,
{
image:require('../../../src/assets/group/kids_room3.png')
}
]
}
Trigger image( like when press button(i select image random number betwenn 0-2))
let setImage=>(){
this.setState({newimage:this.state.randomImages[Math.floor(Math.random() * 3)];
})
}
view
<Image
style={{ width: 30, height: 30 ,zIndex: 500 }}
source={this.state.newimage}
/>
Hey lads I rounded another way to require It's ugly but works. Images dynamically. Instead of storing your URL in the state you store the entire JSX. For an example:
state = {
image: []
};
Instead of
let imageURL = `'../assets/myImage.png'`
this.state.image = imageURL
You use
let greatImage = (<Image source={require(../assets/myImage.png)}></Image>)
this.state.image = greatImage
To render in the JSX
{this.state.image}
You can style your image in the variable too. I had to use some if statements to render some images dynamically and after break my head for 2 hours this was the way that solved my problem. Like I said It's ugly and probably wrong.
Are you using a module bundler like webpack?
If so, you can try require.ensure()
See: https://webpack.js.org/guides/code-splitting/#dynamic-imports
Reading through the docs, I've found a working answer and I'm able to use dynamic images, in the docs they refer to it as Network Images here
https://facebook.github.io/react-native/docs/images#network-images
Not sure if this can be applied to other file types, but as they list require with non image types
You would need to use the uri: call
data = {uri: urlName}
For me I got images working dynamically with this
<Image source={{uri: image}} />
Try the solution mentioned in this thread for Android. This solves the issue but unfortunately, it's only for android.
But make sure to run react-native run-android after every update. Else, the added images won't appear in the app.
This seems to work :
const {
messages,
} = require(`../${dynamicPath}/messages.specific`);

Rendering React components from text

I'm working on a simple blog/CMS tool. In authoring content, I'm supporting an option to enter raw html/css. The user enters this content into a text area, and I can render this into a page using dangerouslySetInnerHtml. That works fine.
However, I'd really like to embed some React components the content as well. Ideally I'd like to enter something like this into a textarea...
<div>
<p>Some content</p>
<MyPictureComponent url="..." />
</div>
...and then render that into a page and have it create the MyPictureComponent.
I'll be storing the above "code" in a database, and rendering it dynamically as users view the "post". Is it possible to rendering that raw text as functioning React?
I saw this project (HTML to React), which seemed promising, bu that seems to only parse the HTML given to it, not tags for React components.
I found a way to do what I want, with the caveat that it's somewhat manual, and potentially dangerous. However, in my case, I'm creating a blog/CMS for a very limited audience, and the concern about users potentially inserting harmful content is non-existent.
My approach ended up using html-to-react (https://www.npmjs.com/package/html-to-react). Html-to-react accepts a string (containing raw HTML markup), and transforms it into a proper React component. By default, its parse() method doesn't properly handle React components (it just turns them into lower-case-named html elements). However, the library has a parseWithInstructions, which allows you to control how individual nodes in the component are rendered.
In my case, I want to enable certain React components to be rendered. One of those is my ExternalLink component. What follows is the method I use to transform some user-entered raw HTML into a React component that properly rendered my components.
updatePreview() {
// Combine the user-entered CSS and the user-entered HTML into a single string.
let outputPreview = "<div><style>" + this.state.cssValue + "</style><div>" + this.state.inputValue + "</div></div>";
let htmlToReactParser = new HtmlToReact.Parser();
let processNodeDefinitions = new HtmlToReact.ProcessNodeDefinitions(React);
let processingInstructions = [
{
// Custom <ExternalLink> processing
shouldProcessNode: function (node) {
return node.name === 'externallink';
},
processNode: function (node, children) {
let attribs = node.attribs;
return <ExternalLink {...attribs}>{children}</ExternalLink>
}
},
{
// Anything else
shouldProcessNode: function (node) {
return true;
},
processNode: processNodeDefinitions.processDefaultNode
}];
// Convert the HTML into a React component
let reactComponent = htmlToReactParser.parseWithInstructions(outputPreview, () => true,
processingInstructions);
// Now that we have a react component, we set it to the state.
// Our render() method includes a "{this.state.outputPreview}", which causes the
// component to be rendered.
this.setState({outputPreview: reactComponent, refreshPreviewTimer: null});
}
Note that outputString in the first line of the method will contain some raw text like this:
"<div>
<style></style>
<div>
<p>Here's a link:<p>
<ExternalLink url="http://www.google.com">Google</ExternalLink>
</div>
</div>"
There are some approaches I'll take to generalize this approach more, using a dictionary of strings to enable support for a wider range of Components. I'll also look at some approach to automatically importing the desired Component. (Currently, I'm manually importing all supported components.)
So, all credit goes to the author of html-to-react, though I may encourage him to include an example of rendering child components.

How to configure Material-UI Autocomplete with secondaryText on dynamic data

I'm facing a issue and I feels like lacking documentation around this.
I'm build a site where I get an AutoComplete component to search for data in the database, and it's working fine, it gets the data, I've build validation working on this, everything goes ok.
If I use only one Text to render on the MenuItem it works fine, like display a name property I've got from database, but if I try to display something like Name as a primaryText and Size as a SecondaryText it simply doesn't render the autocomplete menuItem results, even being filled correctly.
For the record I'm trying to achieve something like this: https://cloud.githubusercontent.com/assets/9424409/17258323/33764c38-557b-11e6-808c-ac22287703d0.gif
But I can only make component work with something like this: https://cloud.githubusercontent.com/assets/9424409/17258376/66015990-557b-11e6-8c12-9016da6e1f2e.gif
Here is the code as it works and render my data, using only textKey:
this.dataSourceConfig = {text: 'textKey', value: 'valueKey', validationKey:'validationKey'};
this.state = {
dataSourceDrug:[{textKey: 'Text data goes here', valueKey: "", validationKey:'validation value goes here'}]}
render(){
return(
<AutoComplete
onNewRequest={this.onDrugNewRequest}
onUpdateInput={this.handleDrugUpdateInput}
searchText={this.state.valueDrug}
dataSource={this.state.dataSourceDrug}
dataSourceConfig={this.dataSourceConfig}
/>
)
}
So how can I configure this to work rendering primary and secondary text?
I've checked docs and even issues on git but it doesn't says much to me:
http://www.material-ui.com/#/components/auto-complete
https://github.com/callemall/material-ui/issues/4852
https://github.com/callemall/material-ui/blob/master/docs/src/app/components/pages/components/AutoComplete/ExampleDataSources.js
Ok, I found the answer thanks to the light #awzx answer brought to me.
I was working with dataSourceConfig and dataSource, and to work woth primary and secondary text, it does NOT work with dataSourceConfig, so I removed the attribute from my component
dataSourceConfig={this.dataSourceConfig}
And in my for to place the data I've done this:
var updatedDataSource = [];
for (var i = 0; i < response.length; ++i)
{
var _name = response[i].name;
var _size = response[i].size;
var _val = <MenuItem primaryText={_name} secondaryText={_size}/>
updatedDataSource.push({text:response[i].name, value:(_val), valueKey:response[i].id, validationKey:'validation string here'});
}
this.setState({dataSourceDrug:updatedDataSource});
You've to configure the secondary text in your dataSource like below (second example in Material UI documentation) :
import React from 'react';
import AutoComplete from 'material-ui/AutoComplete';
import MenuItem from 'material-ui/MenuItem';
const dataSource1 = [
{
text: 'text-value1',
value: (
<MenuItem
primaryText="text-value1"
secondaryText="☺"
/>
),
},
{
text: 'text-value2',
value: (
<MenuItem
primaryText="text-value2"
secondaryText="☺"
/>
),
},
];

Resources