React - passing 'this' as a prop - javascript

Is there any side effect I do not see by doing this ?
class App extends React.Component {
hello() {
console.log("hello")
}
render() {
return <Layout app={this}>
}
}
So later on I can refer to this.props.app.hello (and others) from Layout ?

This is not safe.
React will not know how to watch for changes, so you may miss re-renders. React uses === to check for state changes, and App will always be === to App, even when state or properties change.
Take this example:
class App extends React.Component {
constructor(props) {
super(props);
this.setState({text: 'default value'});
}
hello() {
this.setState({...this.state, text: 'new value'});
}
render() {
return (
<div onClick={this.hello}>
<Layout app={this}>
</div>
);
}
}
class Layout extends React.Component {
render() {
return <div>{this.app.state.text}</div>
}
}
When you click on the parent div, this.hello will be called, but the child component will not detect the state update, and may not re-render as expected. If it does re-render, it will be because the parent did. Relying on this will cause future bugs.
A safer pattern is to pass only what is needed into props:
class App extends React.Component {
//...
render() {
return (
<div onClick={this.hello}>
<Layout text={this.state.text}>
</div>
);
}
}
class Layout extends React.Component {
render() {
return <div>{this.props.text}</div>
}
}
This will update as expected.

Answer
There's nothing wrong in passing functions as props, as I can see in your example, the only thing you have to do is make sure your function is bound to the current component like the following example
Reference
React: Passing Functions to Components

Related

Reactjs : How to change props data from parent component that already distribute to child component?

I just creating a project and use a several component for a page and pass data by using props to each components. The problem is, when I have already change data from parent component to child component by using props and I have update the data from parent component, the child component still using the old data.
The example is just like this:
class Child extends Component{
constructor(props){
super(props);
this.state = {
variabel : props.variable
}
}
render() {
return (
<div>
<h1>{this.state.variable}</h1>
</div>
)
}
}
class Parent extends Component{
constructor(props){
super(props);
this.state = {
variabel : 'Hello'
}
}
render() {
return (
<div>
<Child variable={this.state.variable} />
</div>
)
}
}
So, when I run the page and update the variabel state in Parent Component, Child Component still show the old value. How to make it updated as the Parent Component data? Or I must using Redux for this case?
In general you'll only want to keep one particular piece of state in one place. If you reassign it in the constructor of Child, it will not update when the parent's state updates. So something like this pattern should work:
class Child extends Component{
// Note that no constructor is needed as you are not initializing any state or binding any methods.
render() {
return (
<div>
<h1>{this.props.variable}</h1>
</div>
)
}
}
class Parent extends Component{
constructor(props){
super(props);
this.state = {
variable : 'Hello'
}
}
render() {
return (
<div>
<Child variable={this.state.variable} />
</div>
)
}
}
A warning note about not initializing state with props is in the React docs for constructor, as a matter of fact.
Mitch Lillie's answer is the correct one. You should have only one source of truth.
In general, it's a good idea to keep the state in the nearest common ancestor of the components that depend on the state. Then you pass the props down.
If, however, you need to keep a copy of the prop in the child state, you should use the life cycles that React provides.
Codepen Live Demo
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {
variable: props.variable,
};
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (this.props.variable !== prevState.variable) {
this.setState({
variable: this.props.variable,
});
}
}
render() {
const varState = this.state.variable;
const varProps = this.props.variable;
return (
<div>
Child props: {varProps}
<br />
Child state: {varState}
</div>
);
}
}
class Parent extends React.Component {
constructor(props) {
super(props);
setInterval(this.updateTime, 1000); // refresh every second
this.state = {
variable: new Date().toLocaleString(),
};
}
updateTime = () => {
this.setState({
variable: new Date().toLocaleString(),
});
}
render() {
const time = this.state.variable;
return (
<div>
<div>
Parent: {time}
</div>
<Child variable={time} />
</div>
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById('container')
);

Get default state value by receiving prop data - React

I'm new to react.js.
I'd like to get default value of state following_status by receiving props.user.following_status.
I'm passing user object ( user = { following_status: 'following', id:123 } ) to ReactionButton component. ReactionButton component is looks like this:
class RelationButton extends React.Component {
constructor(props){
super(props);
console.log(props.user.following_status) # undefined!!!
this.state = {
following_status: props.user.following_status
}
...
render() {
if (this.state.following_status == 'following') {
<UnFollowBtn/>
} else {
<FollowBtn/>
}
}
RelationButton was called by UserCardHeader component.
const UserCardHeader = (props) => {
const user = props.user;
return(
<header className="user-card--full__header">
<RelationButton user={user}></RelationButton>
</header>
)
}
I don't understand why console.log(props.user.following_status) returns undefined. I googled many websites like those:
React component initialize state from props
accessing props inside react constructor
those answers suggest
class FirstComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
x: props.initialX
};
}
}
but this didn't work for me.
If I add componentWillReceiveProps to the codes above,
componentWillReceiveProps(props){
console.log(props.user.following_status) #=> "following"
this.setState({following_status: props.user.following_status})
}
everything works well. However I think it's weird solution and sometimes doesn't work. Why can't I receive object props in constructor(props) {} section?
Without the full code, we can't tell what's wrong but it is obvious that following_status comes asynchronously to the component and that's why is not accessible right away in the constructor.
To somehow fix it you can detect if props have changed and reset state accordingly in componentDidUpdate.
class RelationButton extends React.Component {
constructor(props){
super(props);
console.log(props.user.following_status) # undefined!!!
this.state = {
following_status: props.user.following_status
}
}
componentDidUpdate(prevProps) {
if(prevProps.user.following_status !== this.props.user.following_status) {
this.setState({ following_status: this.props.user.following_status })
}
}
render() {
// you forgot about return statements :
if (this.state.following_status == 'following') {
return <UnFollowBtn/>
} else {
return <FollowBtn/>
}
}
}

How to use composition in ReactJs

Well here I want to use one method to another component, And for that I found a way through composition.
And this is what I did for that
file1.js
import ProductList from '../../views/Products/ProductList';
class CloseableTab extends Component {
constructor() {
super();
this.tpItem = () => {
console.log("hello, item clicked");
};
}
render() {
return (
<div>
<ProductList
itemChange={this.tpItem} />
</div>
);
}
}
export default CloseableTab;
Then in productList I want to call the "tpItem" method by calling itemChange in prop.
Though before that I tried to console the 'prop' of product list. So, it shows me null object in the console. And for that I used the code below:
ProductList.js
export default class ProductList extends Component {
constructor() {
super();
};
render() {
console.log(this.props);
return { }
}
}
So, this gives me null object in the console.
I'll appreciate your help, thanks.
Did you make constructor props enabled ?
Just pass props parameter in constructor
constructor(props) {
super(props)
}
The constructor for a React component is called before it is mounted.
When implementing the constructor for a React.Component subclass, you
should call super(props) before any other statement. Otherwise,
this.props will be undefined in the constructor, which can lead to
bugs.
Its not ideal to define functions in the constructor of the component, you can declare them outside of constructor and pass them down, also, in ProductList you are trying to render an object which isn't supported. if you don't want to return anything use return null.
Below code works as expected.
class CloseableTab extends Component {
constructor() {
super();
this.tpItem = () => {
console.log("hello, item clicked");
};
}
render() {
console.log(this.tpItem);
return (
<div>
<ProductList
itemChange={this.tpItem} />
</div>
);
}
}
class ProductList extends Component {
render() {
console.log(this.props);
return null
}
}
However you must write it like
class CloseableTab extends Component {
tpItem = () => {
console.log("hello, item clicked");
};
render() {
console.log(this.tpItem);
return (
<div>
<ProductList
itemChange={this.tpItem} />
</div>
);
}
}
Working sandbox

Reactjs:Is it possible to use Parent component property from another Parent's child component

I have a file named separatefile.jsx, in this file parent component name is Content and child component name is Child.
separatefile.jsx
import React from 'react';
import Parent from './learning.jsx';
class Content extends React.Component {
constructor() {
super();
this.state = {
finding : 'i am finding'
}
}
render() {
return (
<div>
<Child childprop={this.state.finding}/>
<Parent/>
</div>
);
}
}
class Child extends React.Component {
render() {
return (
<div>
<h2>{this.props.childprop}</h2>
<h1>child class property</h1>
</div>
);
}
}
export default Content;
This is another file named as learning.jsx , this file has Parent component named as Parent and Child component named as a Children.
My questions is that i need to access Parent component property(parent component for learning.jsx) from Child component(child component for separatefile.jsx file)...
learning.jsx
import React from 'react';
class Parent extends React.Component {
constructor() {
super();
this.state = {
searching : 'i will find the solution'
}
}
render() {
return (
<div>
<Children childrenprop={this.state.searching}/>
</div>
);
}
}
class Children extends React.Component {
render() {
return (
<div>
<h2>{this.props.childrenprop}</h2>
</div>
);
}
}
export default Parent;
If I understood you correctly, you want to use Parent's state in your Children component?
You can pass it down the component tree as props, e.g.:
class Content extends React.Component {
constructor() {
super();
this.state = {
finding : 'i am finding'
}
}
render() {
return (
<div>
<Child childprop={this.state.finding}/>
<Parent finding={this.state.finding} />
</div>
);
}
}
class Parent extends React.Component {
constructor() {
super();
this.state = {
searching : 'i will find the solution'
}
}
render() {
return (
<div>
<Children finding={this.props.finding} childrenprop={this.state.searching}/>
</div>
);
}
}
class Children extends React.Component {
render() {
return (
<div>
<h2>{this.props.childrenprop}</h2>
<div>{this.props.finding}</div>
</div>
);
}
}
It's probably not a direct answer but if you are starting a new app I would recommend you to use Redux with react-redux.
Redux is a predictable state container for JavaScript apps.
It helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test. On top of that, it provides a great developer experience, such as live code editing combined with a time traveling debugger.
It's very small library so it's easy to understand how everything works. It might be a good solution to your problem.
Todo app example
You can also check out awesome egghead.io free tutorial - Getting Started with Redux
Here is the answer about the redux benefits by its author Dan Abramov
The React documentation provides an answer.
For communication between two components that don't have a
parent-child relationship, you can set up your own global event
system. Subscribe to events in componentDidMount(), unsubscribe in
componentWillUnmount(), and call setState() when you receive an event.
Flux pattern is one of the possible ways to arrange this.

Reactjs, parent component, state and props

I m actually learning reactjs and I m actually developping a little TODO list, wrapped inside of a "parent component" called TODO.
Inside of this parent, I want to get the current state of the TODO from the concerned store, and then pass this state to child component as property.
The problem is that I dont know where to initialize my parent state values.
In fact, I m using ES6 syntax, and so, I dont have getInitialState() function. It's written in the documentation that I should use component constructor to initialize these state values.
The fact is that if I want to initialize the state inside of my constructor, the this.context (Fluxible Context) is undefined actually.
I decided to move the initialization inside of componentDidMount, but it seems to be an anti pattern, and I need another solution. Can you help me ?
Here's my actual code :
import React from 'react';
import TodoTable from './TodoTable';
import ListStore from '../stores/ListStore';
class Todo extends React.Component {
constructor(props){
super(props);
this.state = {listItem:[]};
this._onStoreChange = this._onStoreChange.bind(this);
}
static contextTypes = {
executeAction: React.PropTypes.func.isRequired,
getStore: React.PropTypes.func.isRequired
};
componentDidMount() {
this.setState(this.getStoreState()); // this is what I need to move inside of the constructor
this.context.getStore(ListStore).addChangeListener(this._onStoreChange);
}
componentWillUnmount() {
this.context.getStore(ListStore).removeChangeListener(this._onStoreChange);
}
_onStoreChange () {
this.setState(this.getStoreState());
}
getStoreState() {
return {
listItem: this.context.getStore(ListStore).getItems() // gives undefined
}
}
add(e){
this.context.executeAction(function (actionContext, payload, done) {
actionContext.dispatch('ADD_ITEM', {name:'toto', key:new Date().getTime()});
});
}
render() {
return (
<div>
<button className='waves-effect waves-light btn' onClick={this.add.bind(this)}>Add</button>
<TodoTable listItems={this.state.listItem}></TodoTable>
</div>
);
}
}
export default Todo;
As a Fluxible user you should benefit from Fluxible addons:
connectToStores.
The following example will listen to changes in FooStore and BarStore and pass foo and bar as props to the Component when it is instantiated.
class Component extends React.Component {
render() {
return (
<ul>
<li>{this.props.foo}</li>
<li>{this.props.bar}</li>
</ul>
);
}
}
Component = connectToStores(Component, [FooStore, BarStore], (context, props) => ({
foo: context.getStore(FooStore).getFoo(),
bar: context.getStore(BarStore).getBar()
}));
export default Component;
Look into fluxible example for more details. Code exсerpt:
var connectToStores = require('fluxible-addons-react/connectToStores');
var TodoStore = require('../stores/TodoStore');
...
TodoApp = connectToStores(TodoApp, [TodoStore], function (context, props) {
return {
items: context.getStore(TodoStore).getAll()
};
});
As a result you wouldn't need to call setState, all store data will be in component's props.

Categories

Resources