I am trying to make a react component library so I can use them through out my entire application. I have the first part of the library being a table and this is still in the testing stages but I cannot figure out how to call the component and create it on the page its called. Currently this is how its working. I also just starting programming in React yesterday.
HTML
<script type="text/babel" src="/js/reactcomponents/table.js"></script>
<div id="loadboardContainer">
<div class="table-desc">Future Loads</div>
<div id="futureLoadsContainer"></div>
<div class="table-desc">Todays Loads</div>
<div id ="todaysLoadsContainer"></div>
<div class="table-desc">Active Loads</div>
<div id ="activeLoadsContainer"></div>
</div>
Then I have my react component.
var Table = React.createClass({
getInitialState: function() {
return {
results: [],
columns: []
}
},
componentDidMount: function() {
this.serverRequest = $.get(this.props.source, function(result) {
result = JSON.parse(result);
if(result['resultRows'] == undefined){
result['resultRows'] = [];
}
this.setState({
results: result['resultRows'],
columns: $.makeArray(result['resultCols'])
});
}.bind(this));
},
componentWillUnmount: function(){
this.serverRequest.abort();
},
render: function() {
// Set array for rows.
var rows = [];
var header = [];
this.state.columns.map(function(cols) {
header.push(<TableColumns data={cols.cols} key={cols.id} />);
});
this.state.results.map(function(result) {
rows.push(<TableRow data={result.rows} key={result.id} />);
});
// Return the table.
return (
<table className="table table-condensed table-bordered no-bottom-margin">
<thead>
{header}
</thead>
<tbody>
{rows}
</tbody>
</table>
);
}
});
// Set up columns
var TableColumns = React.createClass({
render: function() {
var colNodes = this.props.data.map(function(col, i){
return (
<th key={i}>{col}</th>
);
});
return (
<tr>
{colNodes}
</tr>
);
}
});
// Set up row
var TableRow = React.createClass({
render: function() {
var rowNodes = this.props.data.map(function(row, i){
return (
<td key={i}>{row}</td>
);
});
return (
<tr>
{rowNodes}
</tr>
);
}
});
var futureContainer = document.getElementById('futureLoadsContainer');
var todaysContainer = document.getElementById('todaysLoadsContainer');
var activeContainer = document.getElementById('activeLoadsContainer');
ReactDOM.render(<Table source="/reactloadboard/react/getFutureTableValues.php" />, futureContainer);
ReactDOM.render(<Table source="/reactloadboard/react/getTodaysTableValues.php" />, todaysContainer);
ReactDOM.render(<Table source="/reactloadboard/react/getActiveTableValues.php" />, activeContainer);
And this works when I import the table.js file to the html but I would like it to be more universal in the component usage and remove the
var futureContainer = document.getElementById('futureLoadsContainer');
var todaysContainer = document.getElementById('todaysLoadsContainer');
var activeContainer = document.getElementById('activeLoadsContainer');
ReactDOM.render(<Table source="/reactloadboard/react/getFutureTableValues.php" />, futureContainer);
ReactDOM.render(<Table source="/reactloadboard/react/getTodaysTableValues.php" />, todaysContainer);
ReactDOM.render(<Table source="/reactloadboard/react/getActiveTableValues.php" />, activeContainer);
to be called in the html/php page like the following.
<script type="text/babel" src="/js/reactcomponents/table.js"></script>
<div id="loadboardContainer">
<div class="table-desc">Future Loads</div>
<div id="futureLoadsContainer"></div>
<div class="table-desc">Todays Loads</div>
<div id ="todaysLoadsContainer"></div>
<div class="table-desc">Active Loads</div>
<div id ="activeLoadsContainer"></div>
</div>
var futureContainer = document.getElementById('futureLoadsContainer');
var todaysContainer = document.getElementById('todaysLoadsContainer');
var activeContainer = document.getElementById('activeLoadsContainer');
ReactDOM.render(<Table source="/reactloadboard/react/getFutureTableValues.php" />, futureContainer);
ReactDOM.render(<Table source="/reactloadboard/react/getTodaysTableValues.php" />, todaysContainer);
ReactDOM.render(<Table source="/reactloadboard/react/getActiveTableValues.php" />, activeContainer);
The first way that is working works fine, but if I remove the ReactDom.renders to the html page I am actually putting the component on it says that Component Table doesn't exist. Is there anyway to do this so later on in the application when I have a table all I have to do is import the table.js react component and connect the source and its there? I have looked at other answers on here but they seem to mainly deal with the data for usability.
In order to use the table component in your HTML you need to export it using module.exports since you are not using ES6 for React.
var Table = React.createClass({
...
});
var TableColumns = React.createClass({
...
});
var TableRow = React.createClass({
...
});
//Export the table component
module.exports = Table;
Also the thing is that browser don't have support for JSX so you need to use either of webpack or browserify to transpile your code.
A good tutorial link Webpack tutorial
Well you are almost there.
Here's how you should get it done:
Table.js
var Table = React.createClass({
//Everything stays same here
});
var TableColumns = React.createClass({
// Stays same here too
});
var TableRow = React.createClass({
// This remains same
});
//Export the table component
export default Table;
HTML
<script type="text/babel" src="/js/reactcomponents/table.js"></script>
<div id="loadboardContainer">
<div class="table-desc">Future Loads</div>
<div id="futureLoadsContainer"></div>
<div class="table-desc">Todays Loads</div>
<div id ="todaysLoadsContainer"></div>
<div class="table-desc">Active Loads</div>
<div id ="activeLoadsContainer"></div>
</div>
<script>
var futureContainer = document.getElementById('futureLoadsContainer');
var todaysContainer = document.getElementById('todaysLoadsContainer');
var activeContainer = document.getElementById('activeLoadsContainer');
ReactDOM.render(<Table source="/reactloadboard/react/getFutureTableValues.php" />, futureContainer);
ReactDOM.render(<Table source="/reactloadboard/react/getTodaysTableValues.php" />, todaysContainer);
ReactDOM.render(<Table source="/reactloadboard/react/getActiveTableValues.php" />, activeContainer);
</script>
There are several ways of making a component reusable with React, but since you recently started learning here few things you can go with.
prefer leaning on actual react app instead of browser, but why ? the code you wrote is es5 code and you can write much cleaner and reusable code in ES6.
for starting use any boilerplate so you don't have to dive into the webpack and other complex things at beginning.
now coming to the point of re-usability, if you use it as webpack, it combines all your code into one js file, which you can implement any where on your website.
Now about your code
check this one , about using separate js file
https://facebook.github.io/react/docs/getting-started.html
it has one important piece of information
Note that some browsers (Chrome, e.g.) will fail to load the Separate
file unless it's served via HTTP.
Related
I created a javascript code to create grid and populate it with cards, using data from json file, and load them into a web page.
This is the code:
// load data into grid container
const container = document.querySelector(".grid");
// get data from the file, using loadData(), inside it populateContainer
function loadData() {
const request = new XMLHttpRequest();
request.open("get", "data.json");
request.onload = () => {
try {
const json = JSON.parse(request.responseText);
populateContainer(json);
} catch (e) {
console.warn("error");
}
};
request.send();
}
function populateContainer(json) {
while (container.firstChild) {
container.removeChild(container.firstChild);
}
json.forEach((row) => {
const card = document.createElement("div");
card.setAttribute("class", `grid-item ${row[7]}`);
card.setAttribute("data-category", `${row[7]}`);
// header
let header = document.createElement("div");
header.setAttribute("class", "card-header");
header.innerHTML = `Current = ${row[1]}$ Original Price = ${row[2]}$ / Discount = ${row[3]}%`;
card.appendChild(header);
// pic
let img = document.createElement("img");
img.setAttribute("class", "card-image");
img.src = `https://${row[6]}`;
card.appendChild(img);
// BODY
let cardBody = document.createElement("div");
cardBody.setAttribute("class", "card-content");
card.appendChild(cardBody);
// -->Title + link
let cardTitle = document.createElement("h4");
cardTitle.setAttribute("class", "card-title");
cardTitle.innerHTML = `<a href='https://${row[4]}'>${row[0]}</a>`;
cardBody.appendChild(cardTitle);
container.appendChild(card);
});
}
document.addEventListener("DOMContentLoaded", () => {
loadData();
});
This is the html body (the javascript script is in main.js file):
<body>
<div id="filters" class=".filter-button-group button-group">
<div class="button All">show all</div>
<div class="button HomeGarden">Home & Garden</div>
<div class="button Electronics">Electronics</div>
<div class="button MomKids">Mom & Kids</div>
<div class="button SportsOutdoor">Sports & Outdoor</div>
<div class="button Accessories">Accessories</div>
<div class="button HealthBeauty">Health & Beauty</div>
</div>
<div class="grid">
</div>
<script src="main.js"></script>
</body>
The code work well, and it create the grid and elements inside it,
but when I want to add a filter for those cards by category, using library like https://listjs.com or https://isotope.metafizzy.co it doesnt work.
How I can apply the filter to my code ?
What issues exactly did occur when trying to filter?
Here is a technical cut-through on how to get your script working by the following steps:
Include Isotope (as a script-tag,using the download-link)
Add "data-filter" attributes in HTML (I added three of them as an example):
<div class="button Electronics" data-filter="[data-category='Electronics']">
Initialize Isotope:
$grid = new Isotope( '.grid',{
itemSelector: '.grid-item',
layoutMode: 'fitRows',
});
Whenever you want to filter, filter. I bound your links to the .arrange-fn using JQuery, but feel free to do it some other way:
$('#filters').on( 'click', 'div.button', function() {
var filterValue = $( this ).attr('data-filter');
$grid.arrange({ filter: filterValue });
});
Did you get any issues trying to do so? I mocked your JSON since I got no service returning it, but in case it's a timing issue, just put the initialization (see above) right below the container.appendChild(card);, that should make it work.
Feel free to ask if I didn't cover some aspect of your question.
Best wishes
I am trying to integrate QuickBlox chat JS sample app into ReactJS app.
The JS sample app has App.js file that looks like this, it uses templates using underscore.js
App.js
function App(config) {
this._config = config;
// Elements
this.page = document.querySelector('#page');
this.userListConteiner = null;
this.init(this._config);
this.loading = true;
}
// Before start working with JS SDK you nead to init it.
App.prototype.init = function (config) {
// Step 1. QB SDK initialization.
QB.init(config.credentials.appId, config.credentials.authKey, config.credentials.authSecret, config.appConfig);
};
App.prototype.loadWelcomeTpl = function () {
var content = document.querySelector('.j-content'),
welcomeTpl = helpers.fillTemplate('tpl_welcome');
helpers.clearView(content);
content.innerHTML = welcomeTpl;
};
// QBconfig was loaded from QBconfig.js file
var app = new App(QBconfig);
Templates in index.html
<script type="text/template" id="tpl_welcome">
<div class="content__title j-content__title j-welcome">
Welcome to QuickBlox chat sample!
</div>
<div class="notifications j-notifications hidden"></div>
<div class="content__inner j-content__inner">
<div class="welcome__message">
<p>Please select you opponent to start chatting.</p>
</div>
</div>
</script>
How do I use the above in my React App
const ChatApp = () => {
return (
);
}
export default ChatApp;
Generally you should get template from
<script type="text/template">
...
</script>
and return it from your component.
Of course you should implement also business code (logic) in component code.
You can split entire sample to few small components like LoginContainer, LoginForm, Dashboard, WelcomeScreen etc...
Good start point are script templates from QuickBlox sample - each template is one React component.
I want to have an API with returns HTML with JSX. When the HTML is loaded, I want to convert this to JSX and pass the props from my
Given this set of code:
import { renderToString } from "react-dom/server";
import ReactDOM from 'react-dom';
function MyHtml (props) {
var apiHtml = renderToString("<div>{this.props.title}</div>");
return (
<div dangerouslySetInnerHTML={{ __html: apiHtml }} />
)
}
export default MyHtml;
ReactDOM.render(
<MyHtml title="test"/>,
document.getElementById('test')
);
I want to have the output
<div>test</div>
Instead, I get
<div>{this.props.title}</div>
Thanks
I think what you're looking for is:
var apiHtml = renderToString(`<div>{${this.props.title}}</div>`);
Using template literals to fill in the code you want.
I ended up doing something like this:
var { title } = this.props; // => testing
var html = ReactDOMServer.renderToString("<div>${title}</div>");
var toRender = eval('`'+html +'`');
To give the output:
<div>testing</div>
Thanks everyone!
I'm trying to set html sent from my server to show inside a div using dangerouslySetInnerHTML property in React. I also have script tag inside it and use functions defined in same inside that html. I have made example of error in JSFiddle here.
This is test code:
var x = '<html><scr'+'ipt>alert("this.is.sparta");function pClicked() {console.log("p is clicked");}</scr'+'ipt><body><p onClick="pClicked()">Hello</p></body></html>';
var Hello = React.createClass({
displayName: 'Hello',
render: function() {
return (<div dangerouslySetInnerHTML={{__html: x}} />);
}
});
I checked and the script tag is added to DOM, but cannot call the functions defined within that script tag. If this is not the correct way is there any other way by which I can inject the script tag's content.
I created a React component that works pretty much like dangerouslySetInnerHtml but additionally it executes all the js code that it finds on the html string, check it out, it might help you:
https://www.npmjs.com/package/dangerously-set-html-content
Here's a bit of a dirty way of getting it done ,
A bit of an explanation as to whats happening here , you extract the script contents via a regex , and only render html using react , then after the component is mounted the content in script tag is run on a global scope.
var x = '<html><scr'+'ipt>alert("this.is.sparta");function pClicked() {console.log("p is clicked");}</scr'+'ipt><body><p onClick="pClicked()">Hello</p></body></html>';
var extractscript=/<script>(.+)<\/script>/gi.exec(x);
x=x.replace(extractscript[0],"");
var Hello = React.createClass({
displayName: 'Hello',
componentDidMount: function() {
// this runs the contents in script tag on a window/global scope
window.eval(extractscript[1]);
},
render: function() {
return (<div dangerouslySetInnerHTML={{__html: x}} />);
}
});
ReactDOM.render(
React.createElement(Hello),
document.getElementById('container')
);
I don't think you need to use concatenation (+) here.
var x = '<html><scr'+'ipt>alert("this.is.sparta");function pClicked() {console.log("p is clicked");}</scr'+'ipt><body><p onClick="pClicked()">Hello</p></body></html>';
I think you can just do:
var x = '<html><script>alert("this.is.sparta");function pClicked() {console.log("p is clicked");}</script><body><p onClick="pClicked()">Hello</p></body></html>';
Since it's passed to dangerouslySetInnerHTML anyway.
But let's get back to the issue. You don't need to use regex to access the script tag's content. If you add id attribute, for example <script id="myId">...</script>, you can easily access the element.
Let's see an example of such implementation.
const x = `
<html>
<script id="myScript">
alert("this.is.sparta");
function pClicked() {console.log("p is clicked");}
</script>
<body>
<p onClick="pClicked()">Hello</p>
</body>
</html>
`;
const Hello = React.createClass({
displayName: 'Hello',
componentDidMount() {
const script = document.getElementById('myScript').innerHTML;
window.eval(script);
}
render() {
return <div dangerouslySetInnerHTML={{__html: x}} />;
}
});
If you have multiple scripts, you can add a data attribute [data-my-script] for example, and then access it using jQuery:
const x = `
<html>
<script data-my-script="">
alert("this.is.sparta");
function pClicked() {console.log("p is clicked");}
</script>
<script data-my-script="">
alert("another script");
</script>
<body>
<p onClick="pClicked()">Hello</p>
</body>
</html>
`;
const Hello = React.createClass({
constructor(props) {
super(props);
this.helloElement = null;
}
displayName: 'Hello',
componentDidMount() {
$(this.helloElement).find('[data-my-script]').each(function forEachScript() {
const script = $(this).text();
window.eval(script);
});
}
render() {
return (
<div
ref={helloElement => (this.helloElement = helloElement)}
dangerouslySetInnerHTML={{__html: x}}
/>
);
}
});
In any case, it's always good to avoid using eval, so another option is to get the text and append a new script tag with the original's script contents instead of calling eval. This answer suggests such approach
a little extension for Dasith's answer for future views...
I had a very similar issue but the in my case I got the HTML from the server side and it took a while (part of reporting solution where backend will render report to html)
so what I did was very similar only that I handled the script running in the componentWillMount() function:
import React from 'react';
import jsreport from 'jsreport-browser-client-dist'
import logo from './logo.svg';
import './App.css';
class App extends React.Component {
constructor() {
super()
this.state = {
report: "",
reportScript: ""
}
}
componentWillMount() {
jsreport.serverUrl = 'http://localhost:5488';
let reportRequest = {template: {shortid: 'HJH11D83ce'}}
// let temp = "this is temp"
jsreport.renderAsync(reportRequest)
.then(res => {
let htmlResponse = res.toString()
let extractedScript = /<script>[\s\S]*<\/script>/g.exec(htmlResponse)[0];
// console.log('html is: ',htmlResponse)
// console.log('script is: ',extractedScript)
this.setState({report: htmlResponse})
this.setState({reportScript: extractedScript})
})
}
render() {
let report = this.state.report
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo"/>
<h2>Welcome to React</h2>
</div>
<div id="reportPlaceholder">
<div dangerouslySetInnerHTML={{__html: report}}/>
</div>
</div>
);
}
componentDidUpdate() {
// this runs the contents in script tag on a window/global scope
let scriptToRun = this.state.reportScript
if (scriptToRun !== undefined) {
//remove <script> and </script> tags since eval expects only code without html tags
let scriptLines = scriptToRun.split("\n")
scriptLines.pop()
scriptLines.shift()
let cleanScript = scriptLines.join("\n")
console.log('running script ',cleanScript)
window.eval(cleanScript)
}
}
}
export default App;
hope this is helpful...
Just use some known XSS tricks. We just had a case where we had to inject a script and couldn't wait for the release so here goes our loader:
<img src onerror="var script = document.createElement('script');script.src = 'http:';document.body.appendChild(script);"/>
I have a simple reactJS component like this :
var LikeCon = React.createClass({
render: function() {
return (
<span>Like</span>
);
}
});
This is placed in a file called Common.jsx. Im trying to use this LinkeCon component from antoher jsx file like this
var FeedTopic = React.createClass({
render: function() {
var test = false;
return (
<div className="topic">
{LikeCon}
</div>
);
}
});
The problem is that this exception is thrown
Error while rendering "FeedBox" to "react1": ReferenceError: LikeCon
is not defined
This is how the import looks like on the Layoutpage
<script src="#Url.Content("~/Scripts/Common.jsx")"></script>
<script src="#Url.Content("~/Scripts/Grid.jsx")"></script>
<script src="#Url.Content("~/Scripts/Feed.jsx")"></script>
My thought was that if Common.jsx that contains the shared component was first, then the var would also be available to the other react components?
Edit :
this is placed on the Layout.cshtml
<script type="text/jsx" src="#Url.Content("~/Scripts/JSXTransformer.js")"></script>
<script type="text/jsx" src="#Url.Content("~/Scripts/Common.jsx")"></script>
<script type="text/jsx" src="#Url.Content("~/Scripts/Grid.jsx")"></script>
<script type="text/jsx" src="#Url.Content("~/Scripts/Feed.jsx")"></script>
The component is now refered to with <LikeCon like="0" /> instead of {LikeCon}.
Edit 2 :
This is how I use the LikeCon
var TopicComments = React.createClass({
render: function() {
var comment = this.props.data.map(function(com, i) {
return (
<article key={i}>
<div className="commentCon">
<div className="tUImgLnk">
<a title={com.UserName} target="_blank" href={com.UserInfoUrl}>
<img className="tUImg" src={com.UserPicSrc} />
</a>
</div>
<b>{com.UserName}</b> :
<span className="content">
{com.Message}
</span>
<div className="status">
<div className="dateCreated dimText">
{com.DateCreated}
</div>
<LikeCon initialLike={com.Like} initialLikeCount={com.LikeCount} objectId={com.Id} categoryKey={1} userId={this.props.userId} />
<article></article>
</div>
</div>
</article>);
}.bind(this));
return(
<div className="comments">
{comment}
</div>
);
}
});
This is how the script import looks like
<script src="http://fb.me/react-0.12.2.js"></script>
<script src="#Url.Content("~/Scripts/jquery-2.1.3.min.js")"></script>
<script src="#Url.Content("~/Scripts/jquery.autosize.min.js")"></script>
<script src="#Url.Content("~/Scripts/spin.min.js")"></script>
<script src="#Url.Content("~/Scripts/JSXTransformer.js")"></script>
<script src="#Url.Content("~/Scripts/Grid.jsx")"></script>
<script src="#Url.Content("~/Scripts/Feed.jsx")"></script>
#RenderSection("ScriptFoot", required: false)
#Html.ReactInitJavaScript()
</body>
This is the exception I get :
Error while rendering "FeedBox" to "react1": ReferenceError: LikeCon
is not defined at React.createClass.render (Script Document
[7]:83:33) -> React.createElement(LikeCon, {initialLike:
this.props.data.Like, i at Script Document [2]:7021:34 at
wrapper (Script Document [2]:12893:21) at Script Document
[2]:6563:14 at wrapper (Script Document [2]:12893:21) at
ReactMultiChild.Mixin.mountChildren (Script Document [2]:12352:42)
at ReactDOMComponent.Mixin._createContentMarkup (Script Document
[2]:7801:32) at Script Document [2]:7723:14 at wrapper (Script
Document [2]:12893:21) at Script Document [2]:6569:44 at wrapper
(Script Document [2]:12893:21) at Script Document [2]:6569:44 at
wrapper (Script Document [2]:12893:21) at Script Document
[2]:13797:38 at Mixin.perform (Script Document [2]:16855:20) at
renderToString (Script Document [2]:13795:24) at Script Document
[9] [temp]:1:7 Line: 7021 Column:34
Add: <script src="Scripts/JSXTransformer.js"></script>
Instead of {LikeCon} use <LikeCon/>
Use type="text/jsx" in your scripts
Make sure you export your LikeCon component, and import it in the file you want to use it in.
var LikeCon = React.createClass({
render: function() {
return (
<span>Like</span>
);
}
});
should be:
class LikeCon extends React.Component{
render() {
return
<span>Like</span>
);
}
}
export default LikeCon
Then on whatever file(s) you wanted to use LikeCon component include this at the top of your file:
import LikeCon from'./path/to/LikeCon.jsx;
Note: my answer is using ES2016...syntax is a tad different.