How to render unknown/variable number of React elements - javascript

I have this React component that looks like this:
var TestResult = React.createFactory(React.createClass({
displayName: 'test-result',
getInitialState: function getInitialState() {
return {
active: false,
testLines: [] //this value will update after AJAX/WebWorker completes
};
},
makeNewState: function makeState(data) {
this.setState(data);
},
componentDidMount: function () {
var self = this;
var testPath = this.props.testPath;
setTimeout(function(){
$.get(testPath).done(function (msg) {
var myWorker = new Worker('/js/workers/one.js');
myWorker.postMessage(msg);
myWorker.onmessage = function (msg) {
self.setState({testLines: msg.data.testLines});
};
}).fail(function (err) {
console.error(err);
});
}, Math.random()*2000);
},
render: function() {
var self = this;
return React.createElement('p', {
className: this.state.active ? 'active' : '',
onClick: this.clickHandler,
},
self.state.testName, ' ', self.state.testLines, React.createElement('b', null, 'Pass/fail')
);
}
}));
what I want is to render a variable number of components in the render function - the variable number is related to number of elements in the testLines array.
So, in order to do that, I might try changing the render method above:
render: function() {
var self = this;
return React.createElement('p', {
className: this.state.active ? 'active' : '',
},
self.state.testName, ' ', React.createElement('div', self.state.testLines, React.createElement('b', null, 'Pass/fail')
);
}
so what I am trying to do is pass testLines, which is an array of React.createElement results, to React.createElement. Is this the correct way of doing it? "It" meaning rendering a variable number of React elements.

What you have to do is map over the array and create each of the sub-elements, then render those:
render: function() {
var lines = this.state.testLines.map(function(line, i) {
// This is just an example - your return will pull information from `line`
// Make sure to always pass a `key` prop when working with dynamic children: https://facebook.github.io/react/docs/multiple-components.html#dynamic-children
return (
<div key={i}>I am a line!</div>
);
});
return (
<div id='lineContainer'>
{lines}
</div>
);
};

Related

How do I render an array (strings) of components in React?

I'm trying to render the array of text when I load the page:
'values': ['hello', 'world']
but it's giving me an error. In my formatoc.js , I have:
var props = {
'name' : 'form',
'timer' : 1500,
'callback' : function(id, validity, value) {console.log(id, validity, value);},
'values': ['hello', 'world'],
'newParent' : new FormatOC.Parent({
"one": {"__array__":"unique", "__type__":"string","__minimum__":1,"__maximum__":200,"__component__":"Input"}
})
}
React.render(React.createElement(ParentComponent, props), document.getElementById('react-component'));
This is what I have in my Parent.jsx file:
define(['react', 'r_foc_node'], function(React, NodeComponent) {
var ParentComponent = React.createClass({
getInitialState: function() {
if(!this.props.newParent) {
throw "ParentComponent requires newParent property";
}
return {
"values": {}
}
},
recursive : function(level) {
that = this;
console.log(level);
for (keys in level) {
console.log("This is a key: ", keys);
if ((typeof level[keys]) === "object") {
if (level[keys].constructor.name === "Parent") {
console.log('I am a parent');
level = level[keys].details();
//console.log(level[keys].get([key_list[0]]));
this.recursive(level);
//console.log(level[keys].keys());
} else {
console.log('I am a node');
//console.log(level[keys]);
};
};
}
},
childChange: function(name, valid, value) {
this.state.values[name] = value;
this.setState(this.state);
console.log(name,value);
// Call parent callback
this.props.callback(
this.props.name,
this.props.newParent.valid(this.state.values),
this.state.values
);
},
render: function() {
var that = this;
var level = this.props;
return (
<div id = "form">
{level.newParent.keys().map(function(k) {
return (
<div>
{(level.newParent.get(k).constructor.name === "Parent") ?
<ParentComponent
name={k}
key={k}
timer={that.props.timer}
callback={that.childChange}
values={that.props.values[k]}
newParent={that.props.newParent.get(k)}
/>
:
<NodeComponent
name={k}
key={that.props.name + '.' + k}
timer={that.props.timer}
callback={that.childChange}
values={that.props.values[k]}
newNode={that.props.newParent.get(k)}
/>
}
</div>
)
})
}
</div>
)
}
I'm not sure if the error is because of the way I'm trying to access the array values? But I can't seem to get the words hello and world to be rendered.
You are using: level.newParent.keys().map(function(k) {...
this should be: level.values.map(function(k) {...

How to check when data changes in component by itself with vue js?

I have method that changes data in itself, simple example:
Vue.component('component', {
template: '#component',
data: function () {
return {
dataToBeWatched: ''
}
},
methods: {
change: function (e) {
var that = this;
setTimeOut(function() {
that.dataToBeWatched = 'data changed';
}, 2000);
},
makeSmthWhenDataChanged: function () {
// ajax request when dataToBeWatched changed or when dataToBeWatched isn't empty
}
}
});
How to create such watcher using correct methods vue js?
Or I need to use props watching it in component?
Vue components can have a watch property which is an object. The object keys need to be the name of the prop or data that needs to be watched, and the value is a function that is invoked when the data changes.
https://v2.vuejs.org/v2/guide/computed.html#Computed-vs-Watched-Property
Vue.component('component', {
template: '#component',
data: function () {
return {
dataToBeWatched: ''
}
},
methods: {
change: function (e) {
var that = this;
setTimeOut(function() {
that.dataToBeWatched = 'data changed';
}, 2000);
},
makeSmthWhenDataChanged: function () {
// ajax request when dataToBeWatched changed or when dataToBeWatched isn't empty
}
},
watch: {
dataToBeWatched: function(val) {
//do something when the data changes.
if (val) {
this.makeSmthWhenDataChanged();
}
}
}
});

Overwrite nested javascript function

I am making a simple chrome extension and want to overwrite a javascript function on a page. The javascript is too complex for me too understand. This is a part of the code:
}), define("components/Payout", ["react", "game-logic/clib", "game-logic/stateLib"], function(e, t, n) {
var r = e.DOM;
return e.createClass({
displayName: "Payout",
mixins: [e.addons.PureRenderMixin],
propTypes: {
engine: e.PropTypes.object.isRequired
},
getInitialState: function() {
return {
payout: 0
}
},
componentDidMount: function() {
window.requestAnimationFrame(this.draw)
},
draw: function() {
if (this.isMounted()) {
var e = t.calcGamePayout(t.getElapsedTimeWithLag(this.props.engine));
e ? this.setState({
payout: e * n.currentPlay(this.props.engine).bet
}) : this.setState({
payout: null
}), window.requestAnimationFrame(this.draw)
}
},
render: function() {
var e = n.currentPlay(this.props.engine).bet < 1e4 ? 2 : 0;
return r.span({
id: "payout"
}, t.formatSatoshis(this.state.payout, e))
}
})
}),
The function i want to 'hijack' is the "render" function. How would i go about that from an external JS file?
What i want is to replace the content of that function with something adjusted.
If the variable obj contains the object returned by that code, you can assign to obj.render:
obj.render = function() {
// your code here
};
Note that your code won't be able to use the r, t, or n variables like the original render function does. Those variables are only available in the original scope, and your function is outside that scope.
If you want to be able to call the original render method, you can do:
var orig_render = obj.render;
obj.render = function() {
var oldrender = orig_render.bind(this);
// your code here, call oldrender() to call original version
};
This is called monkey patching.

Access object context from prototype functions JavaScript

I have problems with object scope.
Here is my class code
// Table list module
function DynamicItemList(data, settings, fields) {
if (!(this instanceof DynamicItemList)) {
return new DynamicItemList(data, settings, fields);
}
this.data = data;
this.settings = settings;
this.fields = fields;
this.dataSet = {
"Result": "OK",
"Records": this.data ? JSON.parse(this.data) : []
};
this.items = this.dataSet["Records"];
this.generateId = makeIdCounter(findMaxInArray(this.dataSet["Records"], "id") + 1);
this.dataHiddenInput = $(this.settings["hidden-input"]);
}
DynamicItemList.RESULT_OK = {"Result": "OK"};
DynamicItemList.RESULT_ERROR = {"Result": "Error", "Message": "Error occurred"};
DynamicItemList.prototype = (function () {
var _self = this;
var fetchItemsList = function (postData, jtParams) {
return _self.dataSet;
};
var createItem = function (item) {
item = parseQueryString(item);
item.id = this.generateId();
_self.items.push(item);
return {
"Result": "OK",
"Record": item
}
};
var removeItem = function (postData) {
_self.items = removeFromArrayByPropertyValue(_self.items, "id", postData.id);
_self.dataSet["Records"] = _self.items;
_self.generateId = makeIdCounter(findMaxInArray(_self.dataSet["Records"], "id") + 1);
return DynamicItemList.RESULT_OK;
};
return {
setupTable: function () {
$(_self.settings["table-container"]).jtable({
title: _self.settings['title'],
actions: {
listAction: fetchItemsList,
deleteAction: removeItem
},
fields: _self.fields
});
},
load: function () {
$(_self.settings['table-container']).jtable('load');
},
submit: function () {
_self.dataHiddenInput.val(JSON.stringify(_self.dataSet["Records"]));
}
};
})();
I have problems with accessing object fields.
I tried to use self to maintain calling scope. But because it is initialized firstly from global scope, I get Window object saved in _self.
Without _self just with this it also doesn't work . Because as I can guess my functions fetchItemsList are called from the jTable context and than this points to Window object, so I get error undefined.
I have tried different ways, but none of them work.
Please suggest how can I solve this problem.
Thx.
UPDATE
Here is version with all method being exposed as public.
// Table list module
function DynamicItemList(data, settings, fields) {
if (!(this instanceof DynamicItemList)) {
return new DynamicItemList(data, settings, fields);
}
this.data = data;
this.settings = settings;
this.fields = fields;
this.dataSet = {
"Result": "OK",
"Records": this.data ? JSON.parse(this.data) : []
};
this.items = this.dataSet["Records"];
this.generateId = makeIdCounter(findMaxInArray(this.dataSet["Records"], "id") + 1);
this.dataHiddenInput = $(this.settings["hidden-input"]);
}
DynamicItemList.RESULT_OK = {"Result": "OK"};
DynamicItemList.RESULT_ERROR = {"Result": "Error", "Message": "Error occurred"};
DynamicItemList.prototype.fetchItemsList = function (postData, jtParams) {
return this.dataSet;
};
DynamicItemList.prototype.createItem = function (item) {
item = parseQueryString(item);
item.id = this.generateId();
this.items.push(item);
return {
"Result": "OK",
"Record": item
}
};
DynamicItemList.prototype.setupTable = function () {
$(this.settings["table-container"]).jtable({
title: this.settings['title'],
actions: this,
fields: this.fields
});
};
DynamicItemList.prototype.load = function () {
$(this.settings['table-container']).jtable('load');
};
DynamicItemList.prototype.submit = function () {
this.dataHiddenInput.val(JSON.stringify(this.dataSet["Records"]));
};
DynamicItemList.prototype.removeItem = function (postData) {
this.items = removeFromArrayByPropertyValue(this.items, "id", postData.id);
this.dataSet["Records"] = this.items;
this.generateId = makeIdCounter(findMaxInArray(this.dataSet["Records"], "id") + 1);
return DynamicItemList.RESULT_OK;
};
DynamicItemList.prototype.updateItem = function (postData) {
postData = parseQueryString(postData);
var indexObjToUpdate = findIndexOfObjByPropertyValue(this.items, "id", postData.id);
if (indexObjToUpdate >= 0) {
this.items[indexObjToUpdate] = postData;
return DynamicItemList.RESULT_OK;
}
else {
return DynamicItemList.RESULT_ERROR;
}
};
Your assigning a function directly to the prototype. DynamicItemList.prototype= Normally it's the form DynamicItemList.prototype.somefunc=
Thanks everyone for help, I've just figured out where is the problem.
As for last version with methods exposed as public.
Problematic part is
$(this.settings["table-container"]).jtable({
title: this.settings['title'],
actions: {
listAction: this.fetchItemsList,
createAction: this.createItem,
updateAction: this.updateItem,
deleteAction: this.removeItem
},
fields: this.fields
});
};
Here new object is created which has no idea about variable of object where it is being created.
I've I changed my code to the following as you can see above.
$(this.settings["table-container"]).jtable({
title: this.settings['title'],
actions: this,
fields: this.fields
});
And now it works like a charm. If this method has drawbacks, please let me know.
My problem was initially in this part and keeping methods private doesn't make any sense because my object is used by another library.
Thx everyone.
You need to make your prototype methods use the this keyword (so that they dyynamically receive the instance they were called upon), but you need to bind the instance in the callbacks that you pass into jtable.
DynamicItemList.prototype.setupTable = function () {
var self = this;
function fetchItemsList(postData, jtParams) {
return self.dataSet;
}
function createItem(item) {
item = parseQueryString(item);
item.id = self.generateId();
self.items.push(item);
return {
"Result": "OK",
"Record": item
};
}
… // other callbacks
$(this.settings["table-container"]).jtable({
title: this.settings['title'],
actions: {
listAction: fetchItemsList,
createAction: createItem,
updateAction: updateItem,
deleteAction: removeItem
},
fields: this.fields
});
};

ReactJs Passing parameters to parent without event handler

i need to pass parameters from child component to parent without event handler like onClick...
My parent component has a method and child component "A" every x second must pass parameters to parent's method. This methods change parent status and pass this value as props to another child component "B".
How can i do this??
this is my code:
var Parent = React.createClass({
getInitialState: function() {
return {
notification: [ {
data: "-",
message: "no notification to show",
type: "-"
} ]
};
},
handleNotification: function(res) {
var notification = this.state.notification;
notification.push(res);
this.setState({ notification: notification });
},
render: function() {
return(
<div>
<Child callFunction={this.handleNotification} />
<NotificationBox
notification={this.state.notification}
/>
</div>
);
}
});
var Child = React.createClass({
displayName: "Child",
handleReturnNotification: function(o) {
this.props.callFunction.bind(o);
},
render: function() {
var msg = "message:" ;
var o = {
data: "-",
message: msg,
type: "Alert"
};
this.handleReturnNotification.bind(this, o);
return (
<div></div>
);
}
});
Pass the parent method as a props. In child A do this.props.parentMethod(parameter);.
add function in useEffect(); to fire it onLoad
useEffect(() => {
props.callFunction()
}, [someVariable]);

Categories

Resources