I'm trying to work through the React Tutorial and have run into a stumbling block. I have the following HTML
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="shortcut icon" href="./src/favicon.ico" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.7/css/materialize.min.css" />
<script src="https://npmcdn.com/react#15.3.1/dist/react.js"></script>
<script src="https://npmcdn.com/react-dom#15.3.1/dist/react-dom.js"></script>
<script src="https://npmcdn.com/babel-core#5.8.38/browser.min.js"></script>
<script src="https://npmcdn.com/jquery#3.1.0/dist/jquery.min.js"></script>
<script src="https://npmcdn.com/remarkable#1.6.2/dist/remarkable.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.7/js/materialize.min.js"></script>
<script type="text/babel" src="src/index.js"></script>
<title>React App</title>
</head>
<body>
<div class="container">
<div class="row">
<div id="sidebar" class="col s3">
</div>
<div id="content" class="col s9">
</div>
</div>
</div>
<script type="text/babel">
</script>
</body>
</html>
With the following javascript file.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
var data = [
{ id: 1, author: "Pete Hunt", text: "This is the 1st comment" },
{ id: 2, author: "Jordan Walke", text: "This is *the 3rd* comment." },
{ id: 3, author: "John Doe", text: "This comment is stupid." }
];
var CommentBox = React.createClass({
getInitialState: function() {
return { data: [] };
},
componentDidMount: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: function(data) {
this.setState({ data: data });
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm />
</div>
);
}
});
var CommentList = React.createClass({
render: function() {
var commentNodes = this.props.data.map(function(comment) {
return (
<Comment author={comment.author} key={comment.id}>
{comment.text}
</Comment>
);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
var CommentForm = React.createClass({
render: function() {
return (
<div className="commentForm">
I'm a Form.
</div>
);
}
});
var Comment = React.createClass({
rawMarkup: function() {
var markdown = new Remarkable();
var rawMarkup = markdown.render(this.props.children.toString());
return { __html: rawMarkup };
},
render: function() {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={this.rawMarkup()} />
</div>
);
}
});
ReactDOM.render(
<CommentBox data={data} />,
document.getElementById('content')
);
I'm using the content created from the Create React App NPM module. When I run the app, I'm told:
Remarkable isn't defined
$ is not defined
Both JQuery and Remarkable are included in my HTML file, and I made sure to put them before my index.js script. Why is it that the app can't find the Types? Is this a React issue, or a Node,js issue? I'm a bit new to web-dev so I'm not sure which component is at fault here or what i'm doing wrong.
Seems like your scripts are loading asynchronously. Waiting for all your scripts to be loadded with jQuery...
(function($){$(function(){
// your react app code here...
})})(jQuery);
Related
I am new in React. Making a simple example, where clicking on button changes state - should rerender children and no of clicks should get updated. But it doesn't and remain '0'. Please help, where is the problem?
index.html
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
<script type="text/babel" src="app.js"></script>
<div id="root"></div>
app.js
var Home = React.createClass({
componentWillMount: function(){
this.btnText = "click Me";
this.commentData = "No comments till now";
this.clickNo = 0;
},
render: function(){
return (
<div>
<ButtonComponent buttonText={this.btnText} clickNumber={this.clickNo} handleclick={this.clickHandler} />
<CommentComponent commentText={this.commentData} />
</div>
)
},
clickHandler: function(){
this.setState({clickNo: this.state.clickNo++});
}
});
var ButtonComponent = React.createClass({
render: function(){
return (
<button onClick={this.props.handleClick}>{this.props.buttonText}{this.props.clickNumber}</button>
)
}
});
var CommentComponent = React.createClass({
render: function(){
return (
<p>{this.props.commentText}</p>
)
}
})
ReactDOM.render(<Home />, document.getElementById('root'));
Result:
I am trying to make a todo app in react. But it keeps double posting. I made an edit function and after that it keeps double posting. I have followed the example of the guide. But I can not find the error.
<!DOCTYPE html>
<html>
<head>
<title>EASY SHIT</title>
<meta charset="utf-8">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<script src="../../build/react.js"></script>
<script src="../../build/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.24/browser.min.js"></script>
<style>
body{
margin-top:30px;
}
a.delete{
color: red;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-12">
<div id="app"></div>
</div>
</div>
</div>
<script type="text/babel">
var App = React.createClass({
getInitialState: function (){
return{
text: '',
isEdit: 0,
todos:[
{
id: 1,
text: 'meeting at work'
},
{
id: 2,
text: 'Walk the dog'
},
{
id: 3,
text: 'Eat candy'
}
]
}
},
render: function(){
return(
<div>
<TodoForm
{...this.state}
changeText={this.handleChangeText}
onTodoUpdate={this.handleTodoUpdate}
onTodoAdd={this.handleTodoAdd} />
<TodoList
{...this.state}
editTodo={this.handleTodoEdit}
deleteTodo={this.handleTodoDelete}/>
</div>
)
},
handleTodoAdd: function(text){
var newTodo = {
id: this.state.todos.length + 1,
text: text
}
this.setState({todos: this.state.todos.concat(newTodo)});
},
handleTodoDelete: function(todo){
var todos = this.state.todos;
for(var i = 0;i <todos.length; i++) {
if(todos[i].id == todo.id){
todos.splice(i, 1);
}
}
this.setState({todos: todos});
},
handleTodoEdit: function(todo){
this.setState({
text: todo.text,
isEdit: todo.id
});
},
handleChangeText: function(text){
this.setState({text: text});
},
handleTodoUpdate: function(todo){
var todos = this.state.todos;
for(var i = 0;i <todos.length; i++) {
if(todos[i].id == todo.id){
todos.splice(i, 1);
}
}
todos.push(todo);
this.setState({todos: todos});
}
});
var TodoForm = React.createClass({
render: function(){
return(
<div>
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label>Todo text</label>
<input type="text" ref="text" value={this.props.text} onChange={this.onChange} className="form-control" />
</div>
</form>
</div>
)
},
onChange: function(e){
this.props.changeText(e.target.value);
},
onSubmit: function(e){
e.preventDefault();
var text = this.refs.text.value.trim();
if(!text){
alert('Please enter something');
return;
}
if(this.props.isEdit){
var updatedTodo = {
id:this.props.isEdit,
text: text
}
this.props.onTodoUpdate(updatedTodo);
} else {
this.props.onTodoAdd(text);
}
this.props.onTodoAdd(text);
this.refs.text.value= '';
}
});
var TodoList = React.createClass({
render: function(){
return(
<ul className="list-group">
{
this.props.todos.map(todo => {
return <li className="list-group-item" todo={todo} key ={todo.id}>
<span onClick={this.editTodo.bind(this, todo)}> {todo.text}</span> <a onClick={this.OnDelete.bind(this, todo)}className="delete" href="#">x</a></li>
})
}
</ul>
)
},
OnDelete: function(todo){
this.props.deleteTodo(todo);
},
editTodo: function(todo){
this.props.editTodo(todo);
}
});
ReactDOM.render(
<App />,
document.getElementById('app')
);
</script>
</body>
</html>
It looks to me like you aren't tracking whether you are editing an existing item or adding a new one.
If you're editing, then you need to track which one, then replace it or update it in your state, otherwise append to the list. You're just appending, so your app always thinks a new one is being added and hence it looks like it's double posting, but it is actually adding a brand new item that happens to have updated text.
The other option is to call delete and then edit, which will have the same effect.
Html:
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>React Tutorial</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src='https://cdn.firebase.com/js/client/2.2.1/firebase.js'></script>
</head>
<body>
<div id="content">
<!--<h1>Comments</h1>-->
<!--<ul id="comments-list"></ul>-->
</div>
<!--<div id="input">-->
<!--<input type="text" placeholder="Author" id="author-field">-->
<!--<input type="text" placeholder="Comment" id="comment-field">-->
<!--</div>-->
<script type="text/babel" src="example.js"></script>
</body>
</html>
Js:
var Comment = React.createClass({
render: function() {
return (
<div className="comment">
<h4 className="commentAuthor">
{this.props.author}
</h4>
<p>{this.props.comment}</p>
</div>
);
}
});
var CommentBox = React.createClass({
getInitialState: function () {
return {data: [], author: "", comment: ""}
},
componentWillMount: function() {
this.commentsDB = new Firebase('https://burning-fire-9280.firebaseio.com/comments');
this.commentsDB.on("child_added", function(snap) {
var nextData = this.state.data.concat(snap.val());
this.setState(
{data: nextData, comment: ""}
)
}).bind(this)
},
onAuthorChange: function(e) {
e.preventDefault();
this.setState({
author: e.target.value
});
},
onCommentChange: function (e) {
e.preventDefault();
this.setState({
comment: e.target.value
});
},
handleSubmit: function (e) {
this.commentsDB.push({
author: this.state.author, comment: this.state.comment, date: getEpoch()
})
},
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm onAuthorChange={this.onAuthorChange} onCommentChange={this.onCommentChange} state={this.state}/>
</div>
);
}
});
var CommentList = React.createClass({
render: function() {
var commentNodes = this.props.data.map(function(entry) {
return (
<Comment author={entry.author} comment={entry.comment}>
</Comment>
);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
var CommentForm = React.createClass({
render: function() {
return (
<form onSubmit={this.props.handleSubmit}>
<input id="author-field" onChange={this.props.onAuthorChange} value={this.props.state.author} />
<input id="comment-field" onChange={this.props.onCommentChange} value={this.props.state.comment} />
</form>
);
}
});
ReactDOM.render(
<CommentBox />,
document.getElementById('content')
);
Error msg from chrome:
Uncaught TypeError: Cannot read property 'state' of null(anonymous function) # example.js:25(anonymous function) # firebase.js:52Ab # firebase.js:47wb # firebase.js:22xb # firebase.js:21h.Cd # firebase.js:194h.Bd # firebase.js:182Zg.Bd # firebase.js:174(anonymous function) # firebase.js:172Ug # firebase.js:166ua.onmessage # firebase.js:165
firebase.js:40 FIREBASE WARNING: Exception was thrown by user callback. TypeError: Cannot read property 'state' of null
at eval (eval at <anonymous> (https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js:4:28099), <anonymous>:35:26)
at https://cdn.firebase.com/js/client/2.2.1/firebase.js:52:375
at Ab (https://cdn.firebase.com/js/client/2.2.1/firebase.js:47:165)
at wb (https://cdn.firebase.com/js/client/2.2.1/firebase.js:22:216)
at xb (https://cdn.firebase.com/js/client/2.2.1/firebase.js:21:1261)
at Bh.h.Cd (https://cdn.firebase.com/js/client/2.2.1/firebase.js:194:472)
at kh.h.Bd (https://cdn.firebase.com/js/client/2.2.1/firebase.js:182:251)
at Zg.Bd (https://cdn.firebase.com/js/client/2.2.1/firebase.js:174:364)
at Rg.hg (https://cdn.firebase.com/js/client/2.2.1/firebase.js:172:281)
at Ug (https://cdn.firebase.com/js/client/2.2.1/firebase.js:166:464)
It seems to be complaining that the state of the CommentBox node is null, but the I have set the initial state to be {data: [], author: "", comment: ""}, haven't I?
It seems a little bit different. It says "cannot read property state of null" like if this is null (that is quite odd!!).
What is the line 25?
Probably the bind call is in the wrong place:
componentWillMount: function() {
this.commentsDB = new Firebase('https://burning-fire-9280.firebaseio.com/comments');
this.commentsDB.on("child_added", function(snap) {
var nextData = this.state.data.concat(snap.val());
this.setState(
{data: nextData, comment: ""}
)
}.bind(this)) // *** must be attached to the callback inside the parenthesis.
},
Problem: I'm working this tutorial to learn about React.js. I've added the showdown.js file to the project correctly and proved it is loaded as a script file in the client browser. When page loads and I look at the console it shows the error listed in the title.
Environment: MVC 4/5, Reactjs.NET
JSX File Looks Like This:
var Comment = React.createClass({
render: function() {
var converter = new Showdown.converter(); <-- Error is here
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
{converter.makeHtml(this.props.children.toString())}
</div>
);
}
});
var CommentList = React.createClass({
render: function () {
return (
<div className="commentList">
<Comment author="Daniel Lo Nigro">Hello ReactJS.NET World!</Comment>
<Comment author="Pete Hunt">This is one comment</Comment>
<Comment author="Jordan Walke">This is *another* comment</Comment>
</div>
);
}
});
var CommentForm = React.createClass({
render: function () {
return (
<div className="commentForm">
Hello, world! I am a CommentForm.
</div>
);
}
});
var CommentBox = React.createClass({
render: function () {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList />
<CommentForm />
</div>
);
}
});
ReactDOM.render(
<CommentBox />,
document.getElementById('content')
);
Proof showdown.js file is sent to client Chrome Browser:
Proof the syntax is correct in JSX file
The line of code where exception is thrown is:
var converter = new Showdown.converter();
Question
How do I get a new instance of Showdown.convertor?
Jan's suggestion above was right. This is what fixed the issue:
<body>
<div id="content"></div>
<script src="https://fb.me/react-0.14.0.min.js"></script>
<script src="https://fb.me/react-dom-0.14.0.min.js"></script>
<script src="#Url.Content(" ~/Scripts/showdown.min.js ")"></script>
<script src="#Url.Content(" ~/Scripts/Tutorial.jsx ")"></script>
</body>
Note that showdown.js must come before the jsx file.
I'm doing the tutorial on the React.js website. Here is my code:
<html>
<head>
<title>Hello React</title>
<script src="http://fb.me/react-0.8.0.js"></script>
<script src="http://fb.me/JSXTransformer-0.8.0.js"></script>
<script src="http://code.jquery.com/jquery-1.10.0.min.js"></script>
</head>
<body>
<div id="content"></div>
<script type="text/jsx">
/**
* #jsx React.DOM
*/
// The above declaration must remain intact at the top of the script.
// Your code here
var commentsData = [
{author: "Pete Hunt", text: "This is one comment"},
{author: "Jordan Walke", text: "This is *another* comment"}
];
var CommmentBox = React.createClass({
getInitialState: function() {
return {data: []};
},
componentWillMount: function() {
this.loadComments();
},
render: function() {
return (
<div className='commmentBox'>
<h1>Comments</h1>
<CommentList data={this.state.data} />
<br />
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
</div>
);
},
handleCommentSubmit: function(comment) {
commentsData.push(comment);
this.loadComments();
},
loadComments: function() {
console.log(commentsData.length);
this.setState({data: commentsData});
}
});
var CommentList = React.createClass({
render: function() {
var commentNodes = this.props.data.map(function(comment) {
return <Comment author={comment.author} text={comment.text} />;
});
return (
<div className='commentList'>
{commentNodes}
</div>
);
}
});
var CommentForm = React.createClass({
render: function() {
return (
<form className='commentForm' onSubmit='handleSubmit'>
<input type='text' placeholder='Your name' ref='author'/>
<input type='text' placeholder='Your comment' ref='text'/>
<input type='submit' value='Post' />
</form>
);
},
handleSubmit: function() {
var author = this.refs.author.getDOMNode().value.trim();
var text = this.refs.text.getDOMNode().value.trim();
this.props.onCommentSubmit({author: author, text: text});
this.refs.author.getDOMNode().value = '';
this.refs.text.getDOMNode().value = '';
return false;
}
});
var Comment = React.createClass({
render: function() {
return(
<div className='comment'>
<br />
<h3 className='commentAuthor'>
{this.props.author} wrote:
</h3>
<h3 className='commentText'>
{this.props.text}
</h3>
</div>
);
}
});
React.renderComponent(
<CommmentBox />,
document.getElementById('content')
);
</script>
</body>
</html>
When I add comments, they don't show up in the comments list. I'm logging the length of the comment array to the console, and it never changes. What am I doing wrong?
You need to do this because you were using a string in onSubmit event.
<form className='commentForm' onSubmit={this.handleSubmit}>
You had this in your sample code:
<form className='commentForm' onSubmit='handleSubmit'>
Your code caused a Uncaught TypeError: string is not a function error. Because of that error it was not hitting the handleSubmit function and also caused the browser to reload.