Ref.Current is Undefined - reactjs

TLDR - I need help figuring out how to change a subcomponent color using refs.
I'm trying to teach myself a little more about React refs by doing a simple example: comparing a background color change in subcomponents with both props and refs. I realize this is not a best practice in the wild, however, for a toy example, it seemed like a good isolated exercise.
import React from 'react';
import logo from './logo.svg';
import './App.css';
import SubComponent1 from './SubComponent1'
import SubComponent2 from './SubComponent2'
class App extends React.Component {
render() {
let myRef = React.createRef();
return (
<div className="App">
<header className="App-header">
<SubComponent1
message = "Passing via props"
color = "orange"
/>
<SubComponent2
message = "Passing via ref"
ref={myRef}
/>
{console.log("hi")}
{console.log(myRef)}
{console.log(myRef.current)}
{/*{myRef.current.style = { backgroundColor: 'green' }}*/}
</header>
</div>
);
}
}
export default App;
I would like to be able to call myRef.current.style = { backgroundColor: 'green' } (or something to that effect) in my App.js file, however, it seems like myRef.current is null when I try to call it.
When I console log, I get {current : null}, but upon expanding, the component data is there. I read this may be because myRef.current gets wiped after compomentDidMount, but I'm not really sure where to go from here.
If I wanted to go about completing this example, what would be the best way for me to do so? Ideally, I think I'd like to be able to call the line I have commented out or something like it.
Code - https://github.com/ericadohring/ReactRef

you have to define the ref variable in component, not under the render function.
component ... {
myRef = React.createRef();
...
render() {
...
...
}
}

Related

How to change the style of a reactjs component by code

I need to change the style of some child components of a react components. Something like this:
import React, { Component } from 'react';
class Parent extends Component {
onClickHandler = (event) => {
this.props.children[0].props.style.marginLeft = "-100%";
}
render() {
<div onClick={this.onClickHandler}>
{this.props.children}
</div>
}
}
export default Parent;
The error i'm getting is:
TypeError: Cannot add property marginLeft, object is not extensible
May you help me guys?
Thanks a lot !!
The error you are getting is because you cannot modify props, since those are immutable. A simpler approach can be done using plain CSS and simple state management.
With this technique, you need a state variable to know when to add the class modifier. That class modifier is in charge of overriding the styles of the child component.
The JS would look like this:
import React, { Component } from "react";
class Parent extends Component {
constructor() {
this.state = {
bigMargin: false
};
}
onClickHandler = (event) => {
event.preventDefault();
this.setState({ bigMargin: true });
};
render() {
return (
<div className={`parent-class ${bigMargin && 'big-margin'}`} onClick={this.onClickHandler}>
{this.props.children}
</div>
);
}
}
export default Parent;
And the CSS can be something as simple as this (or as complex as you may want)
.big-margin:first-child {
margin-left: -100%;
}
React props are immutable and you can't change them, they are read only and you can't add new properties.
This is done via Object.preventExtensions, Object.seal and Object.freeze.
To "fix" the error partialy you should define marginLeft in the first child of your Parent component
<Parent>
<p style={{marginLeft: '0'}}>1</p>
<p>2</p>
</Parent>
You will get now a new Error :
TypeError: "marginLeft" is read-only
Imagine having the ability to change props, and you pass the same prop to many children, one of them change it value, this will lead to unexpected behavior.
Try something like
Grab the element by its id on click
document.getElementById("demo").style.marginLeft = '-100px'
Or use react refs to grab the element

Passing {ClassName} vs passing {<ClassName>} as react component porps. What is the difference?

I'm new to react development. I often came accross code examples where a component takes other component as props, like so:
import AccountCircleIcon from '#material-ui/icons/AccountCircle';
import { UserInfo } from './userinfo';
const UserPanel = (props) => {
return (
<UserInfo icon={<AccountCircleIcon/>} user={props.user}>
{props.children}
</UserInfo>
);
}
Other time, I see component that takes class name as one of its props. Like so:
import { Admin } from 'react-admin';
import { AppLayout } from './components';
const App = (props) => {
return (
<Admin layout={AppLayout}>
{* other codes here ... *}
</Admin>
);
}
What's the difference? If both approach can be used to achieve the same effect, which one is considered better?
The difference is that on the first code, you're calling the render function on your component and passing the result render as a prop. On the second code, you're sending the component (not rendered yet!) as a prop and will use it to render in another place of your tree.
Essentially they're the same and it's up to how you want to drive passing props on your project.
Just to illustrate, consider that you have two components:
// Returns a JSX.Element
function AccountCircleIcon() {
return (<>Foo</>)
}
// Also, returns a JSX.Element
function AppLayout() {
return (<>Bar</>)
}
function App() {
return (
<>
// The two lines below will produce exactly a JSX.Element
<AccountCircleIcon/>
{AppLayout()}
// Dependency prop here will be a valid React component
// Like a function you can call: AppLayout()
<AnotherComponent dependency={AppLayout}/>
// Dependency prop will be also valid React component
// but it's already rendered.
<AnotherComponent dependency={<AccountCircleIcon/>}/>
</>
)
}
Check this out to understand and play a little bit with the idea.

React.js - how to pass event handlers to deeply nested component without props drilling?

I have the structure of components (nested) that seems like this:
Container
ComponentA
ComponentB
ComponentC(want to handle event here with state that lives on container)
Do I need to pass as props all the way from Container, ComponentA, ComponentB and finally ComponentC to have this handler? Or is there another way like using Context API?
I'm finding a bit hard to handle events with react.js vs vue.js/angular.js because of this.
I would recommend using either Context API (as you mentioned) or Higher Order Components (HoC)
Context Api is your data center. You put all the data and click events that your application needs here and then with "Consumer" method you fetch them in any component regardless of how nested it is. Here is a basic example:
context.js //in your src folder.
import React, { Component, createContext } from "react";
import { storeProducts } from "./data"; //imported the data from data.js
const ProductContext = createContext(); //created context object
class ProductProvider extends Component {
state = {
products: storeProducts,
};
render() {
return (
<ProductContext.Provider
//we pass the data via value prop. anything here is accessible
value={{
...this.state,
addToCart: this.addToCart //I wont use this in the example because it would
be very long code, I wanna show you that, we pass data and event handlers here!
}}
>
// allows all the components access the data provided here
{this.props.children},
</ProductContext.Provider>
);
}
}
const ProductConsumer = ProductContext.Consumer;
export { ProductProvider, ProductConsumer };
Now we set up our data center with .Consumer and .Provider methods so we can access
here via "ProductConsumer" in our components. Let's say you want to display all your products in your home page.
ProductList.js
import React, { Component } from "react";
import Product from "./Product";
import { ProductConsumer } from "../context";
class ProductList extends Component {
render() {
return (
<React.Fragment>
<div className="container">
<div className="row">
<ProductConsumer>
//we fetch data here, pass the value as an argument of the function
{value => {
return value.products.map(product => {
return <Product key={product.id} />;
});
}}
</ProductConsumer>
</div>
</div>
</React.Fragment>
);
}
}
export default ProductList;
This is the logic behind the Context Api. It sounds scary but if you know the logic it is very simple. Instead of creating your data and events handlers inside of each component and prop drilling which is a big headache, just put data and your event handlers here and orchestrate them.
I hope it helps.

What is the difference between passing a function as a prop with or without parentheses in React?

This is probably something that I should know but I don't quite understand the behavior of my component when I pass a function without parentheses. Here's my component code.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import AppBar from 'material-ui/AppBar';
import LoginButton from './LoginButton';
import LogoutButton from './LogoutButton';
class Header extends Component {
renderButton() {
switch (this.props.auth) {
case null:
return
case false:
return <LoginButton />
default:
return <LogoutButton />
}
}
handleTitleClick() {
return(
<Link to={this.props.auth ? '/classes' : '/'}>
QueueMe
</Link>
);
}
render() {
const styles = {
title: {
cursor: 'pointer',
},
};
return(
<AppBar
title={<span style={styles.title}>QueueMe</span>}
onTitleClick={this.handleTitleClick()}
iconElementRight={this.renderButton()}
showMenuIconButton={false}
/>
);
}
}
/*
* #input: redux state
* Allows the component to access certain piece of the state as props
* Reducers determine the key in the state
*/
function mapStateToProps(state) {
return { auth: state.auth };
}
export default connect(mapStateToProps)(Header);
For my onTitleClick property in <AppBar>, I get the expected behavior when I pass it handleTitleClick() but when I pass it handleTitleClick and click it, I get an error that says Cannot read property 'auth' of undefined. What exactly is the difference here that causes the handleTitleClick not to be aware of the state?
Good question! There are a few things wrong going on here. Javascript this can be a real pain. The problem is that your functions are not bound.
When you write onTitleClick={this.handleTitleClick()} you are immediately invoking the function at compile time. When you pass it handleTitleClick when you are providing an unbound function, it has no this defined.
There are two potential solutions, you can either define
handleTitleClick = (event) =>
return(
<Link to={this.props.auth ? '/classes' : '/'}>
QueueMe
</Link>
);
}
This makes handleTitleClick an arrow function, arrow functions bind their this to the closure that they were created in.
If you don't like using the IIFE way, you can always use
constructor(props) {
super(props)
this.handleTitleClick = this.handleTitleClick.bind(this)
}
Check this out if you're still stuck.
https://medium.freecodecamp.org/react-binding-patterns-5-approaches-for-handling-this-92c651b5af56
You need to bind this to your component.
constructor(props){
super(props);
this.handleTitleClick = this.handleTitleClick.bind(this);
}
After this you can call without the parenthesis. Actually when you call with parenthesis you actually execute the function on render which may not actually the thing you want. You want to call the function only on click not on render. So use without parenthesis and bind your call in the constructor.
<AppBar
...
onTitleClick={this.handleTitleClick}
...
/>

React. how to pass props from onClick to function

I am new to React, I am trying to create an app in which I can click on a button and a function will run countdown timer, but If I pass props from onClick to begin function like this, onClick={begin(props.subject)} the function will run before I click. and if I use onClick with begin without argument, there is no props being passed down. how can I fix that? thanks
import React from 'react';
import SubjectForm from './SubjectForm';
const EditSubject=(props)=>{
return(
<div>
<button onClick={begin}>start</button>
</div>)
};
const begin = (props)=> {
console.log(props.subject)
}
const mapStateToProps=()=>{};
export default connect(mapStateToProps)(EditSubject);
also, is there a way or trick to use a variable inside of begin function from an outside function? so I can make a pause button to pause seInterval in begin function.
You are using functional (stateless) components in this example. You can also use ES6 classes to represent React components, with functions being methods of the class. Then you may make functions like begin in your code as class methods, so they can access class data members like props.
See the code below:
import React from 'react';
import SubjectForm from './SubjectForm';
class EditSubject extends React.Component {
constructor() {
super();
this.begin = this.begin.bind(this);
}
begin() {
console.log(this.props.subject);
}
render() {
return (
<div>
<button onClick={begin}>start</button>
</div>
);
}
};
const mapStateToProps=()=>{};
export default connect(mapStateToProps)(EditSubject);
This is just a best practice if your component has states, and methods. Using functional components like in your example, you may use simply the following:
const EditSubject = (props) => {
return (
<div>
<button
onClick={() => begin(props)} // using props here
>
start
</button>
</div>
);
};
Simple, right ?

Resources