The example here - https://facebook.github.io/react/tips/initial-ajax.html - is modified below.
this.state.username is "placeholder" on load and is passed to <UserGist />
After AJAX loads this.state.username is changed to "octocat", but...
How come this is not passed down to the <UserGist /> component?
var UserGist = React.createClass({
getInitialState: function() {
return {
username: this.props.username
};
},
render: function() {
return (
<div>
{this.state.username}
</div>
);
}
});
var App = React.createClass({
getInitialState: function() {
return {
username: 'placeholder'
};
},
componentDidMount: function() {
this.serverRequest = $.get(this.props.source, function (result) {
var lastGist = result[0];
this.setState({
username: lastGist.owner.login
});
}.bind(this));
},
componentWillUnmount: function() {
this.serverRequest.abort();
},
render: function() {
return (
<div>
<UserGist username={this.state.username} />
</div>
);
}
});
ReactDOM.render(
<App source="https://api.github.com/users/octocat/gists" />,
document.getElementById('main')
);
You are passing the new username to your child component, but you are not updating your child state.
If you want to update your child state when the component receives new props, you should implement it.
UserGist component.
componentWillReceiveProps: function(nextProps){
this.setState({username: nextProps.username});
}
You should access the value via props in your UserGist component as it is getting passed in as a prop:
var UserGist = React.createClass({
render: function() {
return (
<div>
{this.props.username}
</div>
);
}
});
Which can then be represented as a stateless functional component (if you are using React > 0.14):
function UserGist(props) {
return <div>{props.username}</div>
}
It's not necessary to convert it into state unless you are going to be doing some sort of mutation to it within the component itself. It's best to try and use props as far as you can and minimise state.
Some further reading:
state vs props: What is the difference between state and props in React?
Stateless Functional Components
Related
im working with vue & laravel.i have a edit profile page with some forms in it(name,email,...)
the default value of this form not showing for the first time, but if user refresh the page everything will be work!!!
<template>
<label>Name:</label>
<input type="text" v-model="name">
<label>Email:</label>
<input type="email" v-model="email">
<template>
<script>
export default {
data () {
return {
name:'',
email:'',
}
},
mounted : function(){
this.getVueItems();
},
methods: {
getVueItems: function(){
axios.get('./api/auth/me').then(response => {
var vm = this;
vm.name = response.data.name;
vm.email = response.data.email;
});
},
getAuthUser () {
this.user = this.$store.getters.currentUser
},
updateAuthUser () {
this.submiting = true,
axios.put('./api/auth/update', {
name:this.name,
email:this.email,
})
.then(response => {
// this.submiting = false;
location.reload(true);
// success();
})
.catch(error => {
this.submiting = false;
})
},
}
}
</script>
whats is the problem?
As you are using arrow function this keyword is already accessible inside the function.
And for this you should first check in console if you are getting proper response value from api in console.
Hence change your function as below and check once.
async getVueItems() {
await axios.get('./api/auth/me').then(response => {
console.log(response);
this.name = response.data.name;
this.email = response.data.email;
});
Im trying to write simple ajax loader and I wondering that i can prevent props.children render in parent container. The problem is that children want to render, no matter that Loader want to show it or not, and if render is based on ajax data that cousing errors.
Example: https://jsfiddle.net/j8dvsq39/
Example2:
This example will produce error couse this.state.data.user is undefined before ajax request.
Loader:
import React from 'react'
export default React.createClass({
getDefaultProps() {
return { text: "Loading", loaded: false };
},
render() {
if(this.props.loaded == false)
return <div>{this.props.text}</div>;
else
return <div>{this.props.children}</div>;
}
})
Class using Loader
import React from 'react'
import Loader from '../helpers/Loader';
import {comm} from '../Comm';
export default React.createClass({
getInitialState() {
return {loaded: false, data: null};
},
componentWillMount(){
comm.get("/xxx/xxx", {json: 1}, (back) => {
console.log(back);
this.setState({loaded: true, data: back});
});
},
render(){
return <Loader loaded={this.state.loaded}>{this.state.data.user.name}</Loader>
});
Reason is, initially you defined data=null and before the ajax call you are using this.state.data.user.name, it will throw the error:
Cannot read property 'name' of undefined
Simple solution is you need to put the check on data until you didn't get the ajax response, Check this:
var Loader = React.createClass({
getDefaultProps() {
return { text: "Loading", loaded: false };
},
render() {
if(this.props.loaded == false)
return <div>{this.props.text}</div>;
else
return <div>{this.props.children}</div>;
}
});
var Hello = React.createClass({
getInitialState() {
return {loaded: false, data: null};
},
componentWillMount(){
setTimeout(()=>{
this.setState({loaded: true, data: {user:{name: "login"}}});
}, 1000);
},
render: function() {
var user = null;
return <Loader loaded={this.state.loaded}>
<div>
Hello {this.state.data ? this.state.data.user.name : null}
</div>
</Loader>;
}
});
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='container'/>
I have a react component - coursePage.js
function getCourseInitState(){
return {
courses: CourseStore.getAllCourses()//courseStore is required in script
};
}
var Courses = React.createClass({
getInitialState: function(){
return getCourseInitState();
},
render: function () {
return (
<div>
<h1> Course </h1>
<CourseList courses={this.state.courses} />
</div>
);
}
});
Action file -courseAction
var CourseAction = {
CourseList: function(){
var courseList = CourseApi.getAllCourses();
Dispatcher.dispatch({
actionType: ActionTypes.COURSE_INITIALIZE,
courseList: courseList
});
}
Store File - courseStore
var CourseStore = assign({}, EventEmitter.prototype, {
addChangeListener: function(callback){
this.on(CHANGE_EVENT, callback);
},
removeChangeListener: function(callback){
this.removeListener(CHANGE_EVENT, callback);
},
emitChange: function(){
this.emit(CHANGE_EVENT);
},
getAllcourses: function(){ //here is the function define
return _courses;
},
getCourseById: function(id){
return _.find(_courses, {id: id});
}
});
Dispatcher.register(function(action){
switch(action.actionType){
case ActionTypes.COURSE_INITIALIZE:
_courses = action.CourseList;
CourseStore.emitChange();
break;
}
});
module.exports = CourseStore;
in console I am getting "Uncaught TypeError: CourseStore.getAllCourses is not a function"
I don't want to call api directly in my coursePage.js so I find this way of initialising the page but it is not working.
(Please note - I am new to this) As per my recent learning Action file must always call API and send the request to State. I can load with help of componentWillMount function. But, I wanted to solve with this.If not wrong, then it is more neat and preferable way of implementing?
You have a typo -> getAllcourses in the Store and in the Component you call getAllCourses
getAllCourses: function(){ //Should be getAllCourses instead of getAllcourses
return _courses;
},
My React component uses data from socket.io as it's state.
I am unsure how to just update the state when the data is updated without re-rendering the entire component.
Example code.
var socket = io();
var data = {
components: [{key: '',name: '',markup: ''}]
};
socket.on('data', function(_) {
data = _;
});
var Components = React.createClass({
displayName: "Components",
getInitialState: function getInitialState() {
return data;
},
handleChange: function handleChange() {
this.setState(data);
},
render: function render() {
/* render */
}
});
ReactDOM.render(
React.createElement(Components, {
data: data
}),
document.getElementById('main')
);
You can add socket event listener to componentDidMount, like this
var socket = io();
var data = {
components: [{key: '',name: '',markup: ''}]
};
var Components = React.createClass({
displayName: "Components",
componentDidMount: function () {
socket.on('data', this.handleData);
},
componentWillUnmount: function () {
socket.removeListener('data', this.handleData);
},
handleData: function (data) {
this.setState(data);
},
getInitialState: function () {
return data;
},
handleChange: function handleChange() {
this.setState(data);
},
render: function render() {}
});
2I have an Ember app which connects to an api from where it gets articles. I make use of pagination to get 10 articles per request. This works. But now I wanted to add sorting to the request. I implemented this by using the extra parameter in the store.find.
However, for some reason if I use the 'return this.store.find('article', params);' instead of 'return this.store.find('article');' new articles (still requested and added correctly to the store!) in the getMore function are not beiing displayed or rendered. But when i remove the params parameter from store.find in model, it does work. What could be the case here?
templates/articles.hbs
<script type="text/x-handlebars" data-template-name="articles">
{{#each itemController="article"}}
<div class="item">
//...
</div>
{{/each}}
</script>
routes/articles.js
import Ember from 'ember';
export default Ember.Route.extend(Ember.UserApp.ProtectedRouteMixin, {
model: function(params) {
var params2 = {page: 1, per_page: 10, sort: params.sort};
return this.store.find('article', params2);
},
setupController: function(controller, model) {
controller.set('content', model);
},
actions:{
//...
},
getMore: function() {
// don't load new data if we already are
//if (this.get('loadingMore')) return;
//this.set('loadingMore', true);
var meta = this.store.metadataFor("article");
if (meta.hasmore) {
var controller = this.get('controller'),
nextPage = controller.get('page') + 1,
perPage = controller.get('perPage'),
sorting = controller.get('sort'),
items;
var params = {page: nextPage, per_page: perPage, sort: sorting};
this.store.findQuery('article', params).then(function (articles) {
controller.set('page', controller.get('page') + 1);
//this.set('loadingMore', false);
});
}
else{
$('#pagination_spinner').hide();
}
},
queryParamsDidChange: function() {
this.refresh();
}
}
});
controllers/articles.js
import Ember from 'ember';
var ArticlesController = Ember.ArrayController.extend({
itemController: 'article',
queryParams: ['sort'],
sort: 'rating',
page: 1,
perPage: 10
});
export default ArticlesController;
views/articles.js
import Ember from 'ember';
export default Ember.View.extend({
didInsertElement: function(){
//this.scheduleMasonry();
this.applyMasonry();
// we want to make sure 'this' inside `didScroll` refers
// to the IndexView, so we use jquery's `proxy` method to bind it
//this.applyMasonry();
$(window).on('scroll', $.proxy(this.didScroll, this));
},
willDestroyElement: function(){
this.destroyMasonry();
// have to use the same argument to `off` that we did to `on`
$(window).off('scroll', $.proxy(this.didScroll, this));
},
// this is called every time we scroll
didScroll: function(){
if (this.isScrolledToBottom()) {
$('#pagination_spinner').addClass('active');
this.get('controller').send('getMore');
}
},
scheduleMasonry: (function(){
Ember.run.scheduleOnce('afterRender', this, this.applyMasonry);
}).observes('controller.model.#each'), //TODO check
applyMasonry: function(){
$('#pagination_spinner').removeClass('active');
var $galleryContainer = $('#galleryContainer');
$galleryContainer.imagesLoaded(function() {
// check if masonry is initialized
var msnry = $galleryContainer.data('masonry');
if ( msnry ) {
msnry.reloadItems();
// disable transition
var transitionDuration = msnry.options.transitionDuration;
msnry.options.transitionDuration = 0;
msnry.layout();
// reset transition
msnry.options.transitionDuration = transitionDuration;
} else {
// init masonry
$galleryContainer.masonry({
itemSelector: '.item',
columnWidth: 0,
"isFitWidth": true
});
}
});
},
destroyMasonry: function(){
$('#galleryContainer').masonry('destroy');
},
// we check if we are at the bottom of the page
isScrolledToBottom: function(){
var distanceToViewportTop = (
$(document).height() - $(window).height());
var viewPortTop = $(document).scrollTop();
if (viewPortTop === 0) {
// if we are at the top of the page, don't do
// the infinite scroll thing
return false;
}
return (viewPortTop - distanceToViewportTop === 0);
}
});
nothing smart coming to my mind, but maybe it's that...
You've got the line:
if (meta.hasmore) {
in your getMore() function. Is this the case that you've got this meta field in one response and forgot in the other?