ref undefined React - javascript

So the problem I am having is that I am trying to print the textContent of my ref every 5 seconds, and this works the very first time typeWrite() is called from componentDidMount(), but when it is called recursively (using setTimeout()), I get an error saying this.intro.current is undefined, even though it was defined the first time the function ran.
I want to keep the structure relatively similar (I don't want to change it too much) because there are other things I have left out that rely on this structure.
export default class Home extends Component {
constructor(props) {
super(props);
this.intro = React.createRef();
}
componentDidMount() {
this.typeWrite();
}
typeWrite() {
console.log(this.intro.current.textContent);
setTimeout(this.typeWrite, 5000);
}
render() {
return (
<div className="intro" ref={this.intro}>Text</div>
)
}
}

You need to bind your function to your component.
constructor(props) {
super(props);
this.intro = React.createRef();
this.typeWrite = this.typeWrite.bind(this);
}
or you need to call your function with arrow function.
typeWrite() {
console.log(this.intro.current.textContent);
setTimeout(() => this.typeWrite(), 5000);
}

Related

creating component instances from array.map() and handing down functions

just a quick query.
I have an array of data points and using this to create instances of a component.
The parent component that creates an array of children components also have some functions I wish to hand down to its children. Is there any way this is possible? Check the code and let me know, I am getting errors.
class App extends React.Component {
constructor(props) {
super(props)
this.handleNext = this.handleNext.bind(this)
}
handleNext() {
// some function that uses setState and will cause component to re render
}
render() {
let children = someArray.map(function(elem, index) {
return (
<ChildComponent name = {elem.name} handDownFunc = {this.handleNext}/>
)
})
return (
{children} // I want them to each be able to access and use handleNext
)
}
}
You got this error, because using function keyword instead of arrow function.
For more infromation please follow this link.
So, it will be someArray.map(() => { return() })

Auto Update value in react component

I'm trying to build a component with auto-updating value based on cookies:
let cookies = 0;
(function count() {
cookies = document.cookie.split("?");
setTimeout(count, 10);
return cookies;
})();
class CartButton extends React.Component {
state = {quantity: cookies.length}
render() {
return (
<Cart onClick={e=>{show_cart()}}>
<Mfont>{this.state.quantity}</Mfont>
<Icon>shopping_cart</Icon>
</Cart>
);
}
}
'count' function works as expected, component is rendered with the latest value returned. Unfortunately, it does not auto-update when 'cookies' are changed. It returns this error:
Warning: render(...): Replacing React-rendered children with a new root component. If you intended to update the children of this node, you should instead have the existing children update their state and render the new components instead of calling ReactDOM.render.
I have tried various variations here but still can't figure it out :/
componentDidMount will get execute only once when your component loads first time. This is the correct place to write any logic which we need to execute after page load.
Try this,
class CartButton extends React.Component {
//It is good to have a constructor for the component which has state
constructor(props){
super(props);
this.state = {quantity: cookies.length}
this.updateQuantity;
}
componentDidMount(){
this.updateQuantity = setInterval(()=> {
cookies = document.cookie.split("?");
this.setState({quantity: cookies.length})
},10)
}
//Don't forget to clear any setInterval like below
componentWillUnmount(){
clearInterval(this.updateQuantity);
}
render() {
return (
<Cart onClick={e=>{show_cart()}}>
<Mfont>{this.state.quantity}</Mfont>
<Icon>shopping_cart</Icon>
</Cart>);
}
}
Here your CartButton is not updating even though count is working fine because CartButton is not listening to your cookies variable. React component updates only when there is either props or state change.
You can something like this..
class CartButton extends React.Component {
state = {quantity: cookies.length}
componentDidMount(){
setInterval(function count() {
cookies = document.cookie.split("?");
this.setState({quantity: cookies})
}.bind(this), 10)
}
render() {
return (
<Cart onClick={e=>{show_cart()}}>
<Mfont>{this.state.quantity}</Mfont>
<Icon>shopping_cart</Icon>
</Cart>);
}
}

How to setState a class object in React?

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' })

Why this.state is undefined in react native?

I am a complete newbie in react native, react.js, and javascript. I am Android developer so would like to give RN a try.
Basically, the difference is in onPress;
This code shows 'undefined' when toggle() runs:
class LoaderBtn extends Component {
constructor(props) {
super(props);
this.state = { loading: false };
}
toggle() {
console.log(this.state);
// let state = this.state.loading;
console.log("Clicked!")
// this.setState({ loading: !state })
}
render() {
return (
<Button style={{ backgroundColor: '#468938' }} onPress={this.toggle}>
<Text>{this.props.text}</Text>
</Button>
);
}
}
but this code works:
class LoaderBtn extends Component {
constructor(props) {
super(props);
this.state = { loading: false };
}
toggle() {
console.log(this.state);
// let state = this.state.loading;
console.log("Clicked!")
// this.setState({ loading: !state })
}
render() {
return (
<Button style={{ backgroundColor: '#468938' }} onPress={() => {this.toggle()}}>
<Text>{this.props.text}</Text>
</Button>
);
}
}
Can you explain me the difference, please?
In Java / Kotlin we have method references, basically it passes the function if signatures are the same, like onPress = () => {} and toggle = () => {}
But in JS it doesn't work :(
The issue is that in the first example toggle() is not bound to the correct this.
You can either bind it in the constructor:
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
...
Or use an instance function (OK under some circumstances):
toggle = () => {
...
}
This approach requires build changes via stage-2 or transform-class-properties.
The caveat with instance property functions is that there's a function created per-component. This is okay if there aren't many of them on the page, but it's something to keep in mind. Some mocking libraries also don't deal with arrow functions particularly well (i.e., arrow functions aren't on the prototype, but on the instance).
This is basic JS; this article regarding React Binding Patterns may help.
I think what is happening is a matter of scope. When you use onPress={this.toggle} this is not what you are expecting in your toggle function. However, arrow functions exhibit different behavior and automatically bind to this. You can also use onPress={this.toggle.bind(this)}.
Further reading -
ES6 Arrow Functions
.bind()
What is happening in this first example is that you have lost scope of "this". Generally what I do is to define all my functions in the constructor like so:
constructor(props) {
super(props);
this.state = { loading: false };
this.toggle = this.toggle.bind(this);
}
In the second example, you are using ES6 syntax which will automatically bind this (which is why this works).
Then inside of you onPress function, you need to call the function that you built. So it would look something like this,
onPress={this.toggle}

ReactJS - MouseClick gets triggered without a click

I'm new to React.JS and trying to create a click event on an element inside a rendered component.
Here is my code:
class InputPanel extends React.Component{
handleClick(i,j) {
this.props.dispatch(actions.someMethod());
// e.preventDefault();
}
render() {
const { dispatch, board } = this.props;
return(
<div>
{
board.map((row, i) => (
<div>{row.map((cell, j) => <div className="digit"
onClick={this.handleClick(i,j)}>{cell}</div>)}</div>
))
}
</div>
);
}
};
My problem is that "handleClick" gets triggered after page load without any mouse clicked!
I've read about React.JS lifecycle and thought about registering to click event in componentDidMount method, but i'm really not sure about it:
Is there any easier way ? (or: Am I doing something wrong that triggers click ?)
If adding componentDidMount method is the right way - how can I get the element I create in render method ?
You should not use .bind when passing the callback as a prop. There’s a ESLint rule for that. You can read more about how to pass callback without breaking React performance here.
Summary:
make sure you aren’t calling functions but pass functions as handlers in your props.
make sure you do not create functions on every render, for that, you need to bind your handlers in parent component, pass correct the required data (such as indices of iteration) down the child component and have it call the parent’s handler with the data it has
Ideally you’d create another component for the rows and pass the callback there. Moreover, ideally you’d bind the onClick in the parent component’s constructor (or componentWillMount). Otherwise every time render runs a new function is created (in both anonymous function handler () => { this.onClick() } and this.onClick.bind and defeat React’s vdom diff causing every row to rerender every time.
So:
class InputPanel extends React.Component{
constructor() {
super();
this.handleClick = this.handleClick.bind(this);
}
handleClick(i,j) {
this.props.dispatch(actions.someMethod());
// e.preventDefault();
}
render() {
const { dispatch, board } = this.props;
return(
<div>
{board.map((row, i) => <div>
{row.map((cell, j) => <Digit
onClick={this.handleClick})
i={i}
j={j}
cell={cell}
/>)}
</div>)}
</div>
);
}
};
class Digit extends React.Component {
constructor() {
super();
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.props.onClick(this.props.i, this.props.j);
}
render() {
return <div
className="digit"
onClick={this.handleClick}
>{this.props.cell}</div>
}
}
It is because you are calling this.handleClick() function instead of providing a function definition as onClick prop.
Try changing the div line like this:
<div className="digit" onClick={ () => this.handleClick(i,j) }>{cell}</div>
Also you have to bind this.handleClick() function. You can add a constructor and bind all the member functions of a class there. that's the best practice in ES6.
constructor(props, context) {
super(props, context);
this.handleClick = this.handleClick.bind(this);
}
You call this function in render. You should only transfer function and bind params.
onClick={this.handleClick.bind(null,i,j)}
You should use .bind().
class InputPanel extends React.Component{
handleClick(i,j) {
this.props.dispatch(actions.someMethod());
// e.preventDefault();
}
render() {
const { dispatch, board } = this.props;
return(
<div>
{
board.map((row, i) => (
<div>{row.map((cell, j) => <div className="digit"
onClick={this.handleClick.bind(null,i,j)}>{cell}</div>)}</div>
))
}
</div>
);
}
};

Categories

Resources