A questions about this snake game i'm developing with react.
I read on this post about setting function inside the constructor. Now for the example, I want snakeMoves function to call foodGenerator, which is in the constructor, but not in the state, so call this.state inside snakeMoves won't get me foodGenerator.
what options do I have to pass foodGenerator to snakeMoves?
class Play extends Component {
constructor(props) {
super(props)
this.state ={
gameSqueres: [],
food: null
}
this.foodGenerator = this.foodGenerator.bind(this)
}
componentDidMount() {
// create food
this.foodGenerator()
}
foodGenerator() {
console.log('food')
}
// snake move system
snakeMoves = (e) => {
const { gameSqueres } = this.state;
console.log(foodGenerator())
}
If the requirement is just to bind this and get a new function, you can use arrow functions instead. From the method snakeMoves(), simply call: this.foodGenerator()
foodGenerator = () => {
console.log('food')
}
Use this to access class methods.
snakeMoves = (e) => {
const { gameSqueres } = this.state;
console.log(this.foodGenerator())
}
You can simply just do this:
// snake move system
snakeMoves = (e) => {
this.foodGenerator();
}
Related
This is what I do when I want to make this of my class functions bind to the class (component) instance. Is there any easier/more proper way to do this in React?
class App extends Component {
state = {
currentSection: 1,
message :{text:''}
};
constructor(props) {
super(props);
this.prevSection = this.prevSection.bind(this);
this.nextSection = this.nextSection.bind(this);
this.mobileChanged = this.mobileChanged.bind(this);
}
}
If I recall correctly, if you change:
function nextSection() {...}
to
const nextSection = () => {...}
After this change, you can remove this and the bind
Please let me know if your component will remain as functional like it was before. I'm not sure if it this will change the behaviour.
You could use arrow function instead of class method
With arrow function, there will be no this context so you won't have to bind it
class App extends Component {
state = {
currentSection: 1,
message: { text: '' },
};
prevSection = () => {}
nextSection = () => {}
mobileChanged = () => {}
}
Live example:
I tried to call a function in set time inter val for every 5 seconds but i throws errors in
TypeError: this.intialState is not a function
componentDidMount() {
this.intialState();
setInterval(this.changeSelection,5000);
}
changeSelection(){
this.intialState();
}
TypeError: this.intialState is not a function
Updated 5-second countdown using class Clock extends Component
import React, { Component } from 'react';
class Clock extends Component {
constructor(props){
super(props);
this.state = {currentCount: 10}
}
timer() {
this.setState({
currentCount: this.state.currentCount - 1
})
if(this.state.currentCount < 1) {
clearInterval(this.intervalId);
}
}
componentDidMount() {
this.intervalId = setInterval(this.timer.bind(this), 1000);
}
componentWillUnmount(){
clearInterval(this.intervalId);
}
render() {
return(
<div>{this.state.currentCount}</div>
);
}
}
export default Clock;
this loses its context inside functions. You can bind changeSelection in the constructor
constructor() {
super();
this.changeSelection = this.changeSelection.bind(this);
setInterval(this.changeSelection, 500);
}
or make it a fat arrow function since these functions don't have their own this context and will take the parent's
changeSelection = () => {
// code here
}
An arrow function expression is a syntactically compact alternative to a regular function expression, although without its own bindings to the this
componentDidMount() {
this.intialState();
setInterval(this.changeSelection,5000);
}
changeSelection = () => {
this.intialState();
}
The problem is that your function 'changeSelection' does not have access to 'this'.
There are two easy ways to solve this issue:
In your 'constructor', add this line to bind 'this' with changeSelection
this.changeSelection = this.changeSelection.bind()
An arrow function
changeSelection = () => {};
Click here to see more ways to do binding
You can read more about why we need binding why and how to bind
I have an array of objects inside my class that I am modifying and only when a keypress happens do I want to render this object visually.
class Example extends React.Component {
constructor(props) {
super(props);
this.myArr = []; // this is an array of objects
}
render() {
return (
???
);
}
}
Now I modify the contents of this.myArr in many different methods. And only when I'm ready (on a keypress or some other event) do I want to render it.
Now in my render() should I have a reference to this.myArr and then use this.forceUpdate() when I want to force a re-render.
Or should I move myArr into this.state.myArr, and modify this.state.myArr in my methods and when I am ready to display it, in my render() reference to this.state.myArr, and somehow force a rerender with this.setState(myArr: this.state.myArr);
***Second Update - I think this may be what you want. Obviously, you'll need to add a lot of logic for your mouse click events. It should point you in the right direction though.
class Example extends React.Component {
constructor(props) {
super(props);
this.myArr = [];
this.state = {
myArr: [{ width: 10 }, { width: 20 }], // header widths
};
}
// call changeHeaders when needed
// it will update state, which will cause a re-render
changeHeaders = (column, newWidth) => {
const newArr = [...this.state.myArr];
if (newArr[column]) {
newArr[column].width = newWidth;
}
this.setState({ myArr: newArr });
}
renderArray = () => {
return this.state.myArr.map(({ width }) => <div>{width}</div>);
}
render() {
return (
<div>
{this.renderArray()}
</div>
);
}
}
Either way would work but I think its better practice to use this.state to hold your array and use this.setState() to force a re-render and call the this.setState within your keypress event callback
Here is how you update your array value -
Correct modification of state arrays in ReactJS
There's a very good explanation why you should avoid using this.forceUpdate() in here answered by Chris.
In this case you should use state. State is intended to be used for any data that affect how your component looks. Remember that you may not modify your state directly, it's an antipattern. Instead, you should create a copy of the array and update the state with that modified copy. Like so:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {myArr: []}; // this is an array of objects
}
mutateArraySomehow() {
const nextArr = [...this.state.myArr];
nextArr.push('heyyoooo');
this.setState({myArr: nextArr});
}
render() {
return (
???
);
}
}
This is how i would have done it
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
myArr: [],
display: false
}
}
render() {
if(this.state.display) {
return (
<MyComponent onKeyPress=(ev=>{
this.setState({display: true})
})
/>
);
} else {
return (<div></div>)
}
}
}
When there is a modification to the array elements, you need to do a setState of status to true.This would perform the conditional rendering and will display the modified array.
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
myArr = []; // this is an array of objects
status:false
}
}
modifyArr = () => {
//some array modifications
this.setState({status:true})
}
render() {
return (
{this.state.status ? null : (<div>`Display your array here`</div>)}
);
}
}
In short, define state inside class like:
state: {
firstName: String
} = {
firstName: ''
}
And inside render function you would do this:
this.setState({ firstName: 'Junior' })
I wonder how should I approach properties in component's state that depend on another property, for example:
class Sample extends Component {
constructor(props) {
super(props);
this.state = {
mainProperty: 1,
something: {
dependingOnMainProperty: `foo${this.state.mainProperty}bar`
}
}
}
}
Basically, I would like dependingOnMainProperty to be automatically updated when I change mainProperty through setState. Should I write a function for that or is there some other solution?
You may do with getters and setters as follows;
class Component {
constructor(...props){
props.forEach(prop => this[prop] = "whatever");
}
}
class Sample extends Component {
constructor(...props) {
super(...props);
this.state = {
something: {
__mp__: "tester",
dependingOnMainProperty: `foo${this.mainProperty}bar`,
get mainProperty(){
return this.__mp__;
},
set mainProperty(v){
this.__mp__ = v;
this.dependingOnMainProperty = `foo ${v} bar`;
}
}
};
}
}
var s = new Sample("a", "b");
s.state.something.mainProperty = "hello";
console.log(s.state.something.dependingOnMainProperty);
In the following code, when setState is called from campaignsUpdated, render gets logged to the console, but not renderRow:
var React = require('react-native'),
Bus = require('../Bus'),
styles = require('../Styles'),
CampaignsStore = require('../stores/Campaigns'),
CampaignItem = require('./CampaignItem'),
{
Component,
Text,
TextInput,
ListView,
View,
NavigatorIOS,
ActivityIndicatorIOS
} = React
class CampaignList extends Component {
constructor(props) {
super(props)
this.state = {
dataSource: new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2})
}
}
componentDidMount() {
this.addListeners()
Bus.emit('campaigns:search', '')
}
componentWillUnmount() {
this.removeListeners()
}
render() {
console.log('render')
return (
<View style={styles.container}>
<TextInput
style={styles.searchInput}
placeholder='Campaign Name'
value={this.state.campaignName}
onChange={this.campaignSearchChanged.bind(this)}/>
<ListView
dataSource = {this.state.dataSource}
renderRow = {this.renderRow.bind(this)}/>
</View>
)
}
renderRow(campaign) {
console.log('renderRow')
return <CampaignItem campaign={campaign}/>
}
addListeners() {
Bus.on({
'campaigns:updated': this.campaignsUpdated.bind(this)
})
}
removeListeners() {
Bus.off({
'campaigns:updated': this.campaignsUpdated.bind(this)
})
}
campaignsUpdated(event) {
var campaigns = event.data
this.setState({
dataSource: this.state.dataSource.cloneWithRows(campaigns)
})
}
campaignSearchChanged(event) {
var campaignName = event.nativeEvent.text
Bus.emit('campaigns:search', campaignName)
}
}
module.exports = CampaignList
What am I doing wrong here?
You are passing ListView a function renderRow that returns a component. You would have to call that function within ListView once it is passed, presumably during a map over campaigns.
By the looks of it the most likely case is that you have a classic React mutability issue here.
I.e. I suspect your 'campaignsUpdated' method is called with either the same Array instance it received last time, or the elements within the list are the same instances.
Try using:
campaignsUpdated(event) {
var campaigns = event.data.slice(); // <-- clone the array
this.setState({
dataSource: this.state.dataSource.cloneWithRows(campaigns)
})
}
If that doesn't work, then you either make the part that manages your list of compaigns create new copies when changes are made (e.g. const clone = {...campaign, title:"A new Title"}) or update your rowHasChanged method to see if the title (or whatever data you need) has actually changed.
Here are two really good videos about immutability in JavaScript here:
https://egghead.io/lessons/javascript-redux-avoiding-array-mutations-with-concat-slice-and-spread
https://egghead.io/lessons/javascript-redux-avoiding-object-mutations-with-object-assign-and-spread