Function in react does not work as expected - javascript

I am trying to write a function that handles a button click.
I want things to happen:
The function gets a string and changes the state named "chosenGenre" to the given string.
When a certain button is clicked, it calls the function and returns a string.
The string/state "chosenGenre" is saved so I can use it later in another component.
This is what I have wrote but I think the way I deliver the string after the button is clicked isn't right.
import React, { Component } from 'react';
import Genre from './Genre';
import './GenreSelectPage.css';
import Blues from '../Blues.png';
import Classical from '../Classical.png';
import Country from '../Country.png';
export default class GenreSelectPage extends Component{
state = {
chosenGenre: ""
}
handleClick = (genreName) => {
this.setState({chosenGenre: genreName});
}
render(){
return(
<div className = "GenreSelectPage">
<h3>Select your desired Kollab Room Genre</h3>
<Genre genrePicture= {Blues} genreClicked = {this.handleClick("blues")}/>
<Genre genrePicture= {Classical} genreClicked = {this.handleClick("Classical")}/>
<Genre genrePicture= {Country} genreClicked = {this.handleClick("Country")}/>
)
}
}
What should I change in order to call the function in the right way and keep the result?

the problem is this.handleClick("blues") is executed during render, it returns undefined, similar to genreClicked={undefined}, with a side effect to schedule 3 asynchronous setStates...
you can use a factory function that returns an event handler function with access to a closure variable:
makeHandleClick = (genreName) => (event) => {
this.setState({ chosenGenre: genreName });
// console.assert(this.state.chosenGenre); // FYI: not possible, setState is async
}
render() {
...<Genre genrePicture={Blues} genreClicked={this.makeHandleClick("blues")} />

Related

Place data and logic inside or outside React components?

I learn react, and I dont understand what is the difference between placing data or function inside or outside function components ?
What is the best practice, and when use the first or second example ?
Declare in component :
import React from 'react'
const SomeComponent = () => {
const list = ['foo', 'bar']
function add(foo) {
return foo + 1
}
// other logics...
return (
// list...
)
}
Declare outside the component :
import React from 'react'
const list = ['foo', 'bar']
function add(foo) {
return foo + 1
}
const SomeComponent = () => {
// other logics...
return (
// list...
)
}
Have a nice day :)
If your function is a pure function that is not accessing the component's context, you can just put it outside of the component.
It doesn't matter in that case.
But when you need to use this inside the function, it's necessary to define it inside the component.
This post could be helpful too.
Functions in stateless components?
I think the 2nd option is a better cause when react re-renders the "SomeComponent", it doesn't have to create foo() again and again.
If you don't want to use class components like state and other methods than declare outside component else inside the component. In inside component call function as this.functionName

React Class component updates the value for class variable in re-render but not the Function Component

I was playing with ReacJS. I noticed a thing-
In case of Class Component during re-rendering class variable's updated value is updated in screen like:
import React, { Component } from "react";
class Temp extends Component {
constructor(props) {
super(props);
this.count = 0;
this.state = {
foo: 0,
};
}
render() {
return (
<button
onClick={() => {
this.setState({ foo: this.state.foo + 1 });
this.count++;
}}
>
{this.count} - {this.state.foo}
</button>
);
}
}
export default Temp;
But in case of function component the updated value of the ordinary variable is not updated in the screen during re-rendering.
import React, { useRef, useState } from "react";
const RefComponent = () => {
const [stateNumber, setStateNumber] = useState(0);
let refVar = 0;
function incrementAndDelayedLogging() {
setStateNumber(stateNumber + 1);
refVar++;
}
return (
<div>
<button onClick={incrementAndDelayedLogging}>Click</button>
<h4>state: {stateNumber}</h4>
<h4>refVar: {refVar}</h4>
</div>
);
};
export default RefComponent;
Is this how React was implemented or I'm messing around something? I'm curious to know about it.
Thanks
Functional components in React don't have instances, so things like class or instance variables don't necessarily make sense; like others have pointed out in the comments here, React will render (call) functional components and "reset" any local variables that are not explicitly state. Behavior like instance variables for functional components are achieved with useRef.
From the docs:
The useRef() Hook isn’t just for DOM refs. The “ref” object is a generic container whose current property is mutable and can hold any value, similar to an instance property on a class.
This is a consequence of functional components.
Think about it like this: Each time a state var is updated or a prop is updated your function gets called again. All variable declaration will happen again (states are a special case), so let refVar = 0; gets called again.
If you need that variable to live for multiple renders you'll need to declare it in a context that survives re-renders.
You have at least 2 ways of achieving this
declare a state for it with useState
declare it at the module level, but know all your instances of RefComponent will share the same instance

ReactJS - Can't get function to load values from separate file after refactoring

After attempting to factor out functions within my App class in React into separate files, I am dealing with a whirlwind of import/export issues and was wondering if I could get some advice:
First, is factoring out functions from classes within other files/classes a logical thing to do?
Second, this is a specific problem that I'm having:
Expected Behavior: The function MainTable.tableGen() should return a value which App's render() can process.
Actual Behavior: Stepping through the debugger shows that tableGen() is not being fired, but it is recognized. In other words, it is not running the function and returning the value but returning the function as it's printed.
Main.js:
import MainTable from './app/components/input_table/maintable';
export default class App extends React.Component {
constructor(props){
this.MainTable = this.MainTable.bind(this);
}
MainTable(){
MainTable.tableGen(); //Call the function that's in the external file here
}
render(){
return (
<div>
<table>
<tbody onLoad = {this.broadcast()}>
{this.MainTable} //The MainTable.tableGen() function populates the table using a series of loops
</tbody>
</table>
<ButtonMenu onRow = {this.onRowButton} onCol = {this.onColumnButton} undo ={this.onUndoAction} redo = {this.onRedoAction} reset = {this.onResetAction} />
</div>
);
}
}
maintable.js:
class baseTable extends React.Component{
constructor(props){
//properties
super(props);
}
tableGen(){
... function code here...
}
const MainTable = new baseTable;
export default MainTable;
Make sure your tableGen() returns JSX, without quote.
return <h3>Hello World</h3>;
Then, your MainTable function should have return keyword:
MainTable() {
return MainTable.tableGen();
}
And, invoke the function when referencing it in JSX:
{this.MainTable()}

want to execute disNone() function inside saveInfo() function when click on button

I want to execute disNone() inside saveInfo() when the button is clicked:
Error:TypeError: Cannot read property 'disNone' of undefined
import React, { Component } from 'react';
class Login extends Component {
constructor(props){
super(props);
this.state = {
dispNone:"dispNone",
message:"dispNone"
};
this.disNone = this.disNone.bind(this);
};
disNone(){
this.setState({
dispNone: "dispNone",
message:"dispBlock"
});
}
saveInfo(){
this.disNone();
}
render() {
return (
<div>
// other code
<button onClick={this.saveInfo}>Sign up</button>
</div>
);
}
}
export default Login;
In the constructor, besides this.disNone = this.disNone.bind(this), you also need to put
this.saveInfo = this.saveInfo.bind(this);
the error is because safeInfo does not know what this means, which gives you the error that disNone is undefined
EDIT: We do this in the constructor because we only need to bind the function once. Alternatively you could write it in the render function as well, but that means everytime the render function executes, you rebind the the function which is kind of a waste.
A third alternative is to use the () => this.saveInfo() inside the render function, this does not require any kind of binding anywhere, but again, this function has to be "created" every time the render function runs.
add
this.saveInfo = this.saveInfo.bind(this);
in your constructor.
SaveInfo method is not having access to this.

React componentWillReceiveProps not updating state

I've got this React parent component here. The children components at this point are just returning dropdown menus. I expected that componentWillReceiveProps would update the state here, which in turn should be passed to StopList as props. However, when state.selectedSub is changed through handleSubSelect, nothing happens and StopList doesn't receive any props.
Is my mistake with the asynchronous nature of componentWillReceiveProps? Is it in the wrong place in my code? Am I using the wrong lifecycle method?
// We're controlling all of our state here and using children
// components only to return lists and handle AJAX calls.
import React, { Component } from 'react';
import SubList from './SubList';
import StopList from './StopList';
class SubCheck extends Component {
constructor (props) {
super(props);
this.state = {
selectedSub: '--',
selectedStop: null,
stops: ['--'],
};
this.handleSubSelect.bind(this);
this.handleStopSelect.bind(this);
}
// We want the user to be able to select their specific subway
// stop, so obviously a different array of stops needs to be
// loaded for each subway. We're getting those from utils/stops.json.
componentWillReceiveProps(nextProps) {
var stopData = require('../utils/stops');
var stopsArray = [];
var newSub = nextProps.selectedSub
for(var i = 0; i < stopData.length; i++) {
var stop = stopData[i];
if (stop.stop_id.charAt(0) === this.state.selectedSub) {
stopsArray.push(stop.stop_name);
}
}
if (stopsArray.length !== 0 && newSub !== this.state.selectedSub) {
this.setState({stops: stopsArray});
}
}
handleSubSelect(event) {
this.setState({selectedSub:event.target.selectedSub});
}
handleStopSelect(event) {
this.setState({selectedStop:event.target.selectedStop})
}
render() {
return (
<div>
<SubList onSubSelect={this.handleSubSelect.bind(this)}/>
<StopList stops={this.state.stops} onStopSelect={this.handleStopSelect.bind(this)}/>
</div>
);
}
}
export default SubCheck;
You are duplicating data, and causing yourself headaches that aren't necessary.
Both selectedSub and selectedStop are being stored as props and as state attributes. You need to decide where this data lives and put it in a singular location.
The problem you are encountering entirely revolves round the fact that you are changing the state attribute and expecting this to trigger a change to your props. Just because they share a name does not mean they are the same value.

Categories