this keyword in reactJs - node.js

Hi I am confused about the 'this' keyword in ReactJS. I have the following code here for a simple counter in react.
import React, { Component } from "react";
import ReactDOM from "react-dom";
class ButtonClass extends React.Component {
constructor(props) {
super(props);
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
this.state = { count: 0 };
}
increment() {
this.setState({ count: this.state.count + 1 });
}
decrement() {
this.setState({ count: this.state.count - 1 });
}
render() {
const showCountStatus = this.state.count;
return (
<div>
<p>Click to increrment the button</p>
<button onClick={this.increment}>Increment</button>
<button onClick={this.decrement}>Decrement</button>
<h1>{showCountStatus}</h1>
</div>
);
}
}
export default ButtonClass;
ReactDOM.render(<ButtonClass />, document.getElementById("root"));
The code works perfectly however, if I change the increment and decrement function to:
increment() {
this.setState({ count: count+ 1 });
}
decrement() {
this.setState({ count: count-1 });
}
Count is not defined error is shown
Any suggestions as to why? Thank you

As the error message says, there is no count variable defined.
From what I understood, it seems like you want to increment by one from previous state.
Then you can get a reference to the previous state and use that value to increment by one.
increment() {
this.setState(prevState => ({ count: prevState.count+ 1 }));
}
decrement() {
this.setState(prevState => ({ count: prevState.count-1 }));
}
Refer to State Updates May Be Asynchronous for more details on where prevState is from and how it is used.

The code which doesn't work, because count in count + 1, count - 1 is not from this.state object count key. JS is trying to find inside the function scope the variable definition, and it did not find it and throwing the correct error.
So to access the state object key count, you always need to write it like this.state.count.

Related

Use Worker output in a vue component

I tried to send the output from my worker to my component.vue by window.localStorage.
Does anybody know how to show and update my worker's result in my component vue automatically?
This is my code:
worker-api.js
import Worker from "worker-loader!./worker.js";
const worker = new Worker();
worker.addEventListener('message', (e) => {
window.localStorage.setItem('result', JSON.stringify(e.data));
});
export function sendMessage(msg) {
worker.postMessage(msg);
}
worker.js
self.addEventListener("message", (e) => {
var count = e.data;
while(count < 20) {
const result = e.data + 3
self.postMessage(result);
}
});
my-component.vue
<template>
<p>Count: "{{ result }}"</p>
</template>
<script>
import Button from './Button'
import { sendMessage } from './worker-api'
export default {
name: 'my-component',
components: {Button},
data () {
return {
count : 0
}
},
computed: {
result: function () {
return JSON.parse(window.localStorage.getItem('result'))
}
},
methods: {,
postMessage() {
sendMessage(this.count)
}
},
}
</script>
It is not possible to deal with localStorage values as if they were reactive. Probably, that's why your computed property does not work.
One possible solution is to import your worker inside your component and use to update a reactive variable.
Something similar to:
component.vue
<template>
<button #click="increment">Increment Result</button>
{{ result }}
</template>
<script>
export default {
data() {
return {
// the worker path must be relative to the /public folder (in this example, the worker.js file must be at /public/worker.js)
worker: new Worker('/worker.js'),
result: 0
}
},
created() {
const self = this
this.worker.onmessage = function(event) {
self.result = event.data
}
},
methods: {
increment() {
this.worker.postMessage(this.result)
}
}
}
</script>
/public/worker.js
onmessage = function(event) {
// data sent by the Vue component is retrieved from 'data' attribute
postMessage(event.data + 1)
}

React component not rendering after state change

As the title says, when my state changes in my component, the sub components aren't rerendering.
class App extends Component {
constructor() {
super()
this.state = {
url: ""
}
this.handleWorkerSelect = this.handleWorkerSelect.bind(this)
}
handleWorkerSelect(url) {
this.setState({ url })
}
render() {
return (
<div className="App">
<Workers className="workers" handleClick={this.handleWorkerSelect}/>
<HermesWorker url={this.state.url}/>
</div>
)
}
}
const Workers = (props) => {
return (
<div>
<button onClick={() => props.handleClick("http://localhost:5000/api")}>Worker 1</button>
<button onClick={() => props.handleClick("http://localhost:2000/api")}>Worker 2</button>
</div>
)
}
export default App
here is hermesworker.js
class HermesWorker extends Component {
constructor() {
super()
this.state = {
items: [],
visited: [{name: "This Drive", path: "#back", root: ""}]
}
this.handleFolderClick = this.handleFolderClick.bind(this)
this.handleFileClick = this.handleFileClick.bind(this)
}
componentDidMount() {
if (this.props.url.length === 0) return
fetch(this.props.url)
.then(res => res.json())
.then(items => this.setState({ items }))
}
render() {
const folders = this.state.items.map((item) => {
if (!item.isfile) {
return <Card handleClick={this.handleFolderClick} root={item.root} path={item.path} isfile={item.isfile} name={item.name} size={item.size}/>
}
})
const files = this.state.items.map((item) => {
if (item.isfile) {
return <Card handleClick={this.handleFileClick} root={item.root} path={item.path} isfile={item.isfile} name={item.name} s ize={item.size}/>
}
})
const pathButtons = this.state.visited.map((item) => {
return <PathButton handleClick={this.handleFolderClick} root={item.root} path={item.path} name={item.name}/>
})
return (
<div>
{pathButtons}
<div className="flex-container">
{folders}
{files}
</div>
</div>
)
}
}
Essentially the issue is that the HermesWorker component is not being rerendered to use the new url prop. I am not sure why this is happening because for example, in the hermesworker it renders other subcomponents that do get rerendered during a state change.
Any information is appreciated
EDIT updated to add hermes worker, the file is over 100 lines so i cut out and only pasted the stuff I thought was important to the issue, can supply more if needed
I tested that code and it seems to be working fine. Could you provide What is set in HermesWorker component?
Edit: You'll require to set your state with setState on component updates. To do this, you may look for componentDidUpdate, which will run on every update. This is different from componentDidMount, which (hopefully) will run once and then the component may update and re-render, but re-render it's not considered as "mount". So you may try this instead:
constructor(props) {
super(props);
this.state = {
url: '',
items: [],
visited: [{name: "This Drive", path: "#back", root: ""}]
}
this.fetchData = this.fetchData.bind(this);
}
componentDidMount() {
//Mount Once
}
componentDidUpdate(prevProps, prevState) {
if (this.state.url !== this.props.url) {
this.setState({url: this.props.url});
// Url state has changed.
}
if(prevState.url !== this.state.url){
//run your fetch
this.fetchData();
}
}
fetchData(){
if (this.props.url.length === 0) return
fetch(this.props.url)
.then(res => res.json())
.then(items => this.setState({ items }));
}
Note: I moved the fetch to its own function, but that's completly up to you.
Also notice i added url to the state. Make sure to keep your props set to avoid unexpected behaviours.
Edit 2: componentDidUpdate will hand you prevProps and prevState as parameters. With prevProps you get access to whatever props you got on the previous update, and with prevState, as you may guess, you get access to whatever-your-state-was on the previous update. And by "on the previous update" i mean before the update got executed.

Reactjs - this.state.data.map arguments is UNDEFINED

ive been trying to fetch data from my api but its somehow confusing that this.state.Data.map does work but the argument dynamicdata is undefined.
in App.js react
class ShowAll extends Component {
constructor(props){
super(props);
this.state = {
Data: [],
}
}
componentDidMount(){
Request.get('/budget').then((res)=>{
let DataString = JSON.stringify(res.body);
this.setState({
Data: DataString
}, function(){
console.log(DataString);
})
}).catch((err)=> console.log(err));
}
render(){
return(
<div>
{
this.state.Data.map(function(dynamicData, key){
<div>{dynamicData[0]._id}</div> // doesn't render anything and throws an error message saying TypeError: Cannot read property '_id' of undefined
})
}
</div>
)
}
}
**EDIT 1 **
The api data structure is
[{
"_id":"lul",
"_creator":"5a8f8ecdd67afa6494805bef",
"firstItem":"hero",
"secondItem":"30",
"thirdItem":"3",
"__v":0,
"tBudget":9,
"thirdPrice":3,
"secondPrice":3,
"firstPrice":3
}]
Oh. Your original post was a completely different problem.
Two problems. First:
You're mapping, so you don't need to index the mapped value
this.state.Data.map(function(dynamicData, n) {
// dynamicData _is_ the nth element in the array, you don't need dynamicData[x]
dynamicData._id === "lul"
})
second, you're not returning anything from your map callback
map(function(...) {
return (<div>...</div>)
})
Based on the server response, you don't need to access the item 0 when rendering each one of them
class ShowAll extends Component {
constructor(props){
super(props);
this.state = {
Data: [],
}
}
componentDidMount(){
Request.get('/budget').then((res)=>{
this.setState({
Data: res.body, // Assuming res.body is already an array
}, function(){
console.log(DataString);
})
}).catch((err)=> console.log(err));
}
render(){
return(
<div>
{
this.state.Data.map((dynamicData, key) => // Using an arrow function
<div>{dynamicData._id}</div> // Don't access the item 0
)
}
</div>
)
}
}
The previous code assumes res.body is already an array. If that's not the case and you are actually getting a string or something else, you need to parse the response and make sure you assign an array to the state.

How to create a 'Load More' feature without re-rendering entire component in React/Node?

I'm trying to create a simple poll app, where you can make new polls.
In the section 'MyPolls', I want it to render only the first 5 polls that I've made instead of rendering the entire list of polls.
At the bottom is a 'Load More' button, where upon clicking, loads another 5 polls and so on.
I've been using Mongoose/MongoDB backend and my approach has been to use skip and limit.
I've managed to implement this feature, but the problem is the entire component re-renders, which is annoying for a user as you have to scroll down again the click the 'Load More' button.
Here is my app: https://voting-app-drhectapus.herokuapp.com/
(use can you these login details for convenience:
username: riverfish#gmail.com
password: 123)
And then goto the My Polls page.
MyPoll.js:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../actions';
class MyPolls extends Component {
constructor(props) {
super(props);
this.state = {
skip: 0
};
}
componentDidMount() {
this.props.fetchMyPolls(this.state.skip);
this.setState({ skip: this.state.skip + 5 });
}
sumVotes(polls) {
return polls.reduce((a, b) => {
return a.votes + b.votes;
});
}
loadMore(skip) {
this.props.fetchMyPolls(skip);
const nextSkip = this.state.skip + 5;
this.setState({ skip: nextSkip });
}
renderPolls() {
return this.props.polls.map(poll => {
return (
<div className='card' key={poll._id}>
<div className='card-content'>
<span className='card-title'>{poll.title}</span>
<p>Votes: {this.sumVotes(poll.options)}</p>
</div>
</div>
)
})
}
render() {
console.log('polls', this.props.polls);
console.log('skip:', this.state.skip);
return (
<div>
<h2>My Polls</h2>
{this.renderPolls()}
<a href='#' onClick={() => this.loadMore(this.state.skip)}>Load More</a>
</div>
);
}
}
function mapStateToProps({ polls }) {
return { polls }
}
export default connect(mapStateToProps, actions)(MyPolls);
Action creator:
export const fetchMyPolls = (skip) => async dispatch => {
const res = await axios.get(`/api/mypolls/${skip}`);
dispatch({ type: FETCH_MY_POLLS, payload: res.data });
}
Poll route:
app.get('/api/mypolls/:skip', requireLogin, (req, res) => {
console.log(req.params.skip);
Poll.find({ _user: req.user.id })
.sort({ dateCreated: -1 })
.skip(parseInt(req.params.skip))
.limit(5)
.then(polls => {
res.send(polls);
});
});
Entire github repo: https://github.com/drhectapus/voting-app
I understand that might method of implementing this feature might be the best possible solution so I'm open to any suggestions.
It looks like the re-render is triggered by the fact that clicking the "Load More" link actually causes react router to navigate to a new route, causing the entire MyPolls component to re-render.
Just replace the <a href='#' onClick={...}> with <button onClick={...}>.
If you don't want to use a button, you could also change the onClick function to
const onLoadMoreClick = e => {
e.preventDefault(); // this prevents the navigation normally occuring with an <a> element
this.loadMore(this.state.skip);
}

How to render the React component with dynamic data realtime from socket.io high efficiency

My front-end page is made by React + Flux, which sends the script data to back-end nodejs server.
The script data is an Array which contains the linux shell arguments (more than 100000). When to back-end received, it will execute the linux shell command.
Just an example:
cat ~/testfile1
cat ~/testfile2
.
.
.
(100000 times ...etc)
When the backend finished one of the linux shell commands, I can save the readed content to result data. Therefore, socket.io will emit the result data to the front-end.
I want to get the result data from my webpage in real time, so I have done some stuff in my project below.
My React component code:
import React from 'react';
import AppActions from '../../../actions/app-actions';
import SocketStore from '../../../stores/socket-store';
import ResultStore from '../../../stores/result-store';
function getSocket () {
return SocketStore.getSocket();
}
function getResult () {
return ResultStore.getResultItem();
}
class ListResultItem extends React.Component {
constructor () {
super();
}
render () {
return <li>
{this.props.result.get('name')} {this.props.result.get('txt')}
</li>;
}
}
class ShowResult extends React.Component {
constructor () {
super();
this.state = {
socket: getSocket(),
result: getResult()
};
}
componentWillMount () {
ResultStore.addChangeListener(this._onChange.bind(this));
}
_onChange () {
this.setState({
result: getResult()
});
}
render () {
return <div>
<ol>
{this.state.result.map(function(item, index) {
return <ListResultItem key={index} result={item} />;
})}
</ol>
</div>;
}
componentDidMount () {
this.state.socket.on('result', function (data) {
AppActions.addResult(data);
});
}
}
My Flux store (ResultStore) code:
import AppConstants from '../constants/app-constants.js';
import { dispatch, register } from '../dispatchers/app-dispatcher.js';
import { EventEmitter } from 'events';
import Immutable from 'immutable';
const CHANGE_EVENT = 'changeResult';
let _resultItem = Immutable.List();
const _addResult = (result) => {
let immObj = Immutable.fromJS(result);
_resultItem = _resultItem.push(immObj);
}
const _clearResult = () => {
_resultItem = _resultItem.clear();
}
const ResultStore = Object.assign(EventEmitter.prototype, {
emitChange (){
this.emit( CHANGE_EVENT );
},
addChangeListener (callback) {
this.on(CHANGE_EVENT, callback);
},
removeChangeListener (callback) {
this.removeListener(CHANGE_EVENT, callback);
},
getResultItem () {
return _resultItem;
},
dispatcherIndex: register(function (action) {
switch (action.actionType) {
case AppConstants.ADD_RESULT:
_addResult(action.result);
break;
case AppConstants.CLEAR_RESULT:
_clearResult();
break;
}
ResultStore.emitChange();
})
});
However, the page will become very slow after rendering more than 1000 datas. How to enhance the performance for rendering? I need to execute the linux script persistently more than 3 days. Any solutions? Thanks~
Is there any need to render all the data on screen? If not then there are a few ways to deal with cutting down the amount of visible data.
Filter / Search
You can provide a search/filter component that complements the list and creates a predicate function that can be used to determine whether each item should or should not be rendered.
<PredicateList>
<Search />
<Filter />
{this.state.result
.filter(predicate)
.map(function(item, index) {
return <ListResultItem key={index} result={item} />;
})
}
</PredicateList>
Lazy Load
Load the items only when they are asked for. You can work out whether item is needed by keeping track of whether it would be onscreen, or whether the mouse was over it.
var Lazy = React.createClass({
getInitialState: function() {
return { loaded: false };
},
load: function() {
this.setState({ loaded: true });
},
render: function() {
var loaded = this.state.loaded,
component = this.props.children,
lazyContainer = <div onMouseEnter={this.load} />;
return loaded ?
component
lazyContainer;
}
});
Then simply wrap your data items inside these Lazy wrappers to have them render when they are requested.
<Lazy>
<ListResultItem key={index} result={item} />
</Lazy>
This ensures that only data needed by the user is seen. You could also improve the load trigger to work for more complex scenarios, such as when the component has been onscreen for more then 2 seconds.
Pagination
Finally, the last and most tried and tested approach is pagination. Choose a limit for a number of data items that can be shown in one go, then allow users to navigate through the data set in chunks.
var Paginate = React.createClass({
getDefaultProps: function() {
return { items: [], perPage: 100 };
},
getInitialState: function() {
return { page: 0 };
},
next: function() {
this.setState({ page: this.state.page + 1});
},
prev: function() {
this.setState({ page: this.state.page - 1});
},
render: function() {
var perPage = this.props.perPage,
currentPage = this.state.page,
itemCount = this.props.items.length;
var start = currentPage * perPage,
end = Math.min(itemCount, start + perPage);
var selectedItems = this.props.items.slice(start, end);
return (
<div className='pagination'>
{selectedItems.map(function(item, index) {
<ListResultItem key={index} result={item} />
})}
<a onClick={this.prev}>Previous {{this.state.perPage}} items</a>
<a onClick={this.next}>Next {{this.state.perPage}} items</a>
</div>
);
}
});
These are just very rough examples of implementations for managing the rendering of large amounts of data in efficient ways, but hopefully they will make enough sense for you to implement your own solution.

Resources