I can't render my component - reactjs

I need help with React.js.
I have a React Component called ContactsApp, this component call the method componentDidMount() that containts an Ajax Call with recieve props.
componentDidMount(){
jQuery("document").ready(function(){
jQuery("#sent-btn").click(function(ev){
var data_json = {};
data_json['word'] = jQuery("#word").val();
data_json['route'] = jQuery("#route").val();
console.log(data_json['word']);
console.log(data_json['route']);
$.ajax({
type: 'post',
url: 'logic',
data: JSON.stringify(data_json),
contentType: "application/json; charset=utf-8",
traditional: true,
}).then(function(data){
console.log(data);
var list = []
for (var i = 0; i < data.Data.length; i++) {
list.push({"name":data.Data[i].File , "email":data.Data[i].Quantity})
}
this.setState({contacts:list});
}.bind(this))
});
});
}
But my problem is these errors that the web console response for me.
1) Uncaught TypeError: Cannot read property '__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED' of undefined
2) Uncaught TypeError: this.setState is not a function
The class is:
- Container
- ContactsApp
- SearchBar
- ContactList
- ContactItem
Finally I call render method with:
render(<ContactsAppContainer />, document.getElementById('forms'));
I have seen that someone people uses React History, but Cassio Zen don't use this and I think I'm okay.
My import libraries:
import React, { Component, PropTypes } from 'react';
import { render } from 'react-dom';
import 'whatwg-fetch';
import jQuery from 'jquery';
I have app with Go that contains service 'login' for ajax call.
I was using a example based on React component by Cassio Zen.
Sorry my english, I'm learning this, please help me.

You're rebinding the context for this inside your AJAX callback, but you've already lost the component's context as soon as you enter the jQuery ready and click callbacks.
It seems like you're using ES6, so the simple fix would be to use arrow functions instead.
componentDidMount() {
jQuery("document").ready(() => {
jQuery("#sent-btn").click(ev => {
// ...
$.ajax({
// ....
}).then(data => {
// ...
this.setState({contacts:list});
});
});
});
}
However it looks like you've got bigger problems with the other error. I'm not exactly sure what's causing it, but I'm confident that all the while you use jQuery inside your React components, you'll be making everything difficult for yourself.
Honestly, I'd go back a few steps and try and get a better sense of how to use event handlers to listen for button clicks and input changes with React, not with jQuery. Then remove the jQuery dependency from your application and switch the DOM code over to use React and use switch the AJAX code over to using fetch.

Related

Angular v1 Component to Component data transfer

I'm struggling with a problem within Angular v1.6.1 where I am trying to transfer some data from a component to another component.
I have a component called navbar which resides in app\common\navbar has a controller that fetches data from a service. The following files make up navbar component
navbar.component.js
import controller from './navbar.controller';
import template from './navbar.html';
export default navbarComponent = {
restrict: 'E',
bindings: {},
template,
controller
};
navbar.controller.js
class NavbarController {
constructor(httpService) {
const dataUrl = "/assets/data/header.json";
this.name = 'Navbar';
this.headerData = {};
console.log("In "+this.name);
httpService.getData(dataUrl)
.then((response) => {
angular.extend(this.headerData,response.data);
},(error) => { throw error; });
}
}
export default NavbarController;
navbar.html
<section>
<top-header top-data="$ctrl.headerData"></top-header>
<section>
and my httpService resides in app\services folder. It fetches content using axios http library and looks something like this
httpService.js
import axios from 'axios';
export class HttpService {
constructor() {
this.name = "HttpService";
}
getData(api_url){
return axios.get(api_url)
.then((response) => response, (error) => error);
}
}
The component which uses my navbar component's headerData is top-header and resides in app\common\top-header. This is what it contains
top-header.component.js
import template from './top-header.html';
import controller from './top-header.controller';
export default topHeaderComponent = {
restrict: 'E',
template,
bindings: {
topData: '<'
},
controller,
};
top-header.controller.js
class TopHeaderController {
constructor() {
this.name = 'TopHeader';
this.topHeaderData = {};
this.$onInit = function() {
this.topHeaderData = this.topData;
console.log(this.topHeaderData);
console.log(this.topHeaderData.telephoneNumber);
console.log(this.topHeaderData);
}
}
}
export default TopHeaderController;
top-header.html
{{$ctrl.topHeaderData.telephoneNumber}}
and finally my static files resides in assets\data and the JSON I'm trying to fetch header.json contains
header.json
{
"telephoneNumber": 12345678
}
So the problem now I see is that the data does show up in my top-header component but I'm not sure what's happening but the data disappears (comes up undefined) once I try to access the object property.
What I'm saying is that in top-header.controller.js
when I console.log(this.topHeaderData); it shows the object but when I try to console.log(this.topHeaderData.telephoneNumber); it comes up undefined
I think the problem exists because of the execution priority of the Directives. I even set navbar component priority to 5 and it didn't help as the data is undefined.
top-header.controller.js
this.$onInit = function() {
this.topHeaderData = this.topData;
console.log(this.topHeaderData); // shows topData
console.log(this.topHeaderData.telephoneNumber); // undefined
console.log(this.topHeaderData); // shows topData
}
This data this.topHeaderData.telephoneNumber is essential as I use this in my template.
How can I resolve this issue? Any help would be greatly appreciated.
The problem may be in top-header.controller.js: you're assigning binded topData to this.topheaderData in $onInit hook but when component is initialized the data hasn't been fetched yet. Instead of $onInit you should use $onChange hook method which is called by Angular when binded property is updated (in your case when data is fetched from server)
Angular component docs:
$onChanges(changesObj) - Called whenever one-way bindings are updated.
The changesObj is a hash whose keys are the names of the bound
properties that have changed, and the values are an object of the form
{ currentValue, previousValue, isFirstChange() }. Use this hook to
trigger updates within a component such as cloning the bound value to
prevent accidental mutation of the outer value.

Server side rendering React components that loads state through Ajax

I'm struggling to make my component work on both client and server side.
This is the code for my component:
export default class extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
title: props.params.title,
message: props.params.message
};
}
componentDidMount() {
$.ajax({
url: "/get-message",
success: function (result) {
this.setState({
title: result.title,
message: result.message
});
}.bind(this),
cache: false
});
}
render() {
return (
<div>
<h2>{this.state.title}</h2>
<h3>{this.state.message}</h3>
</div>
);
}
}
It works ok. When it gets rendered though client side, it shows both h2 and h3 empty and then they are filled when the ajax call returns.
When it gets rendered though the server, I already get those props filled and the html sent to the client is already completed and don't need anything extra.
The problem is that when it gets rendered on the server, I get the error message on the client:
Warning: React attempted to reuse markup in a container but the checksum was invalid. [...]
It then calls the ajax method again and re-renders everything.
So how should I handle such case? Is there a way to tell React that this component was rendered on the server accept it and not call the componentDidMount method?
A common way to work with this is by using global state. You could serialize a JS object at the bottom of the <body> tag, containing the state calculated on the server side.
<script>globalState={MyFeature: {title: 'foo'}};</script>
Then use that state (or a branch of it) as default for your components.
e.g.
if (globalState.MyFeature.title) {
this.setState({ title: globalState.MyFeature.title });
} else {
$.ajax(/* ... */);
}
Obviously you can use Redux to manage your global state nicely, but you don't really need to. However, there are many useful packages available that will help you streamline this process.
react router redux
redux-async-connect
serialize-javascript - you can use this one without redux

ReactJS: Create components, not using render for non-react libraries

Can you, using React, create router Links (or other components) from data returned from ajax-calls? NOT using the render function.
Background: We have a large 3rd party non-react javascript library that dynamically renders HTML from an AJAX call. We control the input (i.e. the ajax-response), but not the output. I want to input links (a href) and get them rendered as React Router Links. To wrap the non-react component I have created a component which basically have two parts: componentDidMount where I initiate the components and render where I output a div (for the 3rd party javascript library). Ideally we would like to inject reactJS component directly from the ajax-response:
[{'textToRender': '<Link to="/home" title="home">home</Link>'}]
Unfortunately this would only render the text:
<Link to="/home" title="home">home</Link>
Any idea if this is possible?
UPDATE: The non-react component is somewhat complex but for the sake of this question let us say it takes one ajax-url parameter and renders output to that div. The nonreact component expects HTML input from the AJAX, but I have the ability to execute javascript before injecting the HTML into that component.
I.e. the non-react component fetches data from the AJAX call
and update its div.
So the init looks like this:
componentDidMount() {
$('#nonreact').NonReact({ 'ajax': 'http://someurl..', 'transform' : function(data) { //here I can run some JS-code prior to nonrect render; return data; } });
}
And my component render looks like this:
render() {
return (
<div id="nonreact"></div>
)
}
You could do something like this:
componentDidMount() {
$('#nonreact').NonReact({
ajax: 'http://someurl..',
transform: (data) => {
// 1. transform data to some intermediate format
// {to: '/home', title: 'home', text: 'home'}
const {text, ...props} = transform(data);
// 2. create React element and set it to `link` state
this.setState({
link: React.createElement(Link, props, text);
});
}
});
}
render() {
// 3. Use {this.state.link} here
...
}
In React, <Link to="/home" title="home">home</Link> is just the sugar for
React.createElement(
Link,
{ to: "/home", title: "home" },
"home"
);
It does is transformed into that before your code is executed. Simply placing the text <Link to="/home" title="home">home</Link> won't do anything special.
You will probably make your life easier if the response is more like
[{'textToRender': {to: "/home" title: "home" text: "home }}]
Then, in a component that actually renders these you would iterate over these and create Links for that, in render
render: function() {
var linksResponse = ... // how ever you get them
var elements = linkResponses.map(function(entry) {
return Object.keys(entry).map(function(text) {
var linkData = entry[text];
// real action happens here
var link = <Link to={linkData.to} title={linkData.title}>{linkData.text}</Link>;
return link;
});
});
return elements;
}
It's absolutely possible. At my current project I fetch a large chunk of markup from an API. This is normal HTML with a few custom tags in it. These custom tags have corresponding React components in my app. What you want to to is make us of a library such as html-to-react (https://github.com/mikenikles/html-to-react). The way this works is that it parses the markup string and creates an object that represents the DOM for that markup. This object is in turn rendered to a React element. You can have custom tag handlers, telling html-to-react what to do when it finds a non-standard tag in the markup.
It works something like this:
let markupstring = '<div>lots of markup, 100kb</div>' // String
let parsedMarkup = htmlparser.parseWithInstructions(markupstring, function(){return true;}, processingInstructions);
How to configure prosessingInstructions is documented in html-to-react documentation. parsedMarkup is a valid React element.

Async Client Side Rendering React components using Dust JS Templates

I'm having a dust helper called {#component id="compId" path="path/to/react/component"}.
About React component:
The react component makes an ajax call in componentWillMount and calls forceUpdate.
psueodcode:
React.createClass(){
getInitialState : function() {
this.setState({response : "nothing"});
}
var response = "nothing";
componenetWillMount : function(){
//1.performs ajax call
//2.set state in ajax callback this.setState({response : "success"});
//3. calls this.forceUpdate()
},
render : function() {
//returns response text
}
}
About Dust Helper
component is added to dust helper and chunk is written with response from react.
dust.helpers.component = function(chunk, context, bodies, param) {
return chunk.map(function(chunk) {
var domElement = document.getElementById(param.id);
if (!domElement) {
console.log("Document does not present for " + param.id);
domElement = document.createElement(param.id);
}
require([param.path], function(widgetModule) {
console.log("Rendering " + param.path);
var widget = React.createFactory(widgetModule);
React.render(widget(null), domElement, function() {
chunk.end(domElement.innerHTML);
});
});
});
};
Response In Browser:
I'm able to see the response as "nothing" instead of "success". The issue is The render callback is called after the template is rendered on the browser hence the ajax response is not updated.
How would you make dust to listen for changes to a div after the page is rendered? Is it possible in dust, similar to how react finds diff between dom and virtual dom and decides to re-render. I'm using react 0.13.3 and dust 2.7.1

Global React does not play nice with AMD React

I'm getting weird weird behaviour when rendering a component using an AMD-loaded React, when a global React already exists on the page. Click events on components are getting fired when they should not be.
A look at the DOM implies that this stems from multiple React instances (one global, one AMD in my case) not being aware of each other, but this poses a problem when loading an AMD module at runtime that depends on React, into a page that also includes React.
How can I resolve this clash?
Reproduction
I can make a component like this:
var ButtonComponent = React.createClass({
onButtonClick: function(){
alert(this.props.data + ' click event fired');
},
render: function() {
return React.DOM.button({onClick: this.onButtonClick}, this.props.data);
}
});
(function(){ // create vanilla
var ButtonList = React.createClass({
render: function() {
return React.DOM.div({}, React.createElement(ButtonComponent, {data: this.props.data}));
}
});
React.render(React.createElement(ButtonList, {data: 'button that was loaded by the page'}), document.getElementById('page-load-target'));
})();
jsbin
But as soon as I add another component using another instance of React then click the first button, then it calls the click event on the second loaded button:
// .... as above ....
(function(){ // create using amd
require.config({
paths: {
'react': '//fb.me/react-with-addons-0.12.2.min'
}
});
window.setTimeout(function(){
require(['react'], function(ReactFromAmd){
ReactFromAmd.render(ReactFromAmd.createElement(ButtonComponent, {data: 'button that was loaded by AMD'}), document.getElementById('amd-load-target'));
});
}, 1000)
})();
jsbin
If I use the existing, global version of React in this call (rather than ReactFromAmd, then it works as expected. jsbin
The ancestors (React instance) of ButtonComponent and the component created with ReactFromAmd.createElement are different, and yet they are in the same virtual DOM -- That's not allowed.
If you don't mind replacing AMD with browserify, I just figured out a way to let isolated/remotely-loaded React components co-exist nicely.
(To be continued if someone needs it)
This has been fixed in version 0.14.2: http://jsbin.com/tesodoxape/1/edit?html,js,output

Resources