I have a form component (FormComponent) that I need to programmatically focus on when a sibling button component is clicked. I used to be able to call this.refs.myForm.focus() which (given myForm as FormComponent) was written to call .focus() on the first form field inside. However, I've refactored and abstracted some behavior into a higher order component that wraps FormComponent so now, the .focus() method on FormComponent is intercepted by the wrapper component.
Using React 0.14.x (so I don't need ReactDOM.findDOMnode() for DOM components):
// FormComponent
class FormComponentBase extends React.Component {
focus() {
this.refs.firstField.focus();
}
render() {
return (
<form ...>
<input ref="firstField" />
</form>
);
}
}
// Wraps FormComponent in a wrapper component with abstracted/reusable functionality
const FormComponent = hocWrapper(FormComponent);
// OtherComponent
class OtherComponent extends React.Component {
handleClick(ev) {
// ERROR This doesn't work because the hocWrapper component is intercepting the .focus() call
this.refs.myForm.focus();
}
render() {
return (
<div>
<button onClick={this.handleClick.bind(this)}>Focus</button>
<FormComponent ref="myForm" />
</div>
);
}
}
Passing a focus prop and managing the state of whether the form's first element is focused in OtherComponent seems ludicrous. I know you can create get/set properties using defineProperty (or something like that) where accessing an instance property calls a method under the hood to generate the result, but does JS have something like Ruby's missingMethod or PHP's __call where I can define a catch-all method on my hocWrapper that just passes method calls through to the wrapped component? I've run into this issue before with calling other methods through HOC wrapper components but I think I just defined a pass-thru method of the same name on the HOC wrapper (which I'm fairly certain is the wrong way).
Rather than passing focus state down, you can pass the ref up fairly easily with the ref callback function. Here's a fiddle illustrating
class FormComponentBase extends React.Component {
render() {
return (
<form ...>
<input ref={node => this.props.passNode(node)} />
</form>
);
}
}
class OtherComponent extends React.Component {
receiveNode(node) {
if (!this.state.node) this.setState({ node })
}
render() {
return (
<div>
<button onClick={() => this.state.node.focus()}>Focus</button>
<FormComponent passNode={this.receiveNode} />
</div>
);
}
}
UPDATE: a more idiomatic way that doesn't pass the node up. updated fiddle
var FormComponentBase = React.createClass({
render() {
return <input ref={ node =>
node && this.props.shouldFocus && node.focus()
}/>
}
})
var OtherComponent = React.createClass({
getInitialState() {
return { shouldFocus: false }
},
toggleFocus(node) {
// toggle for demonstration
this.setState({ shouldFocus: !this.state.shouldFocus })
},
render: function() {
return (
<div>
<button onClick={() => this.toggleFocus() }>focus</button>
<Child shouldFocus={this.state.shouldFocus} />
</div>
)
}
});
Related
I want to dynamically create child components, receiving an onClick event from their parent/grandparent component in React. During the creation I want to add a parameter to the onClick-event. Basically the desired flow is:
When rendering parent component
Pass the reference to the desired function to the creation of the dynamic component
In process of creating the dynamic component I want to add a parameter, defined by the creator
the onClick event in the child should call the onClick function in the parent using the parameter it got from the creator of the dynamic component
For the code: this is the dynamic component creator and the parent
import React from 'react';
// This is the creator of my dynamic components
// It currently sets this.props.name as parameter for the parent function
class CreateComponent extends React.Component {
render(){
return(
<div className="childBox">
// this.props.component is a react component of type ImageBox (see next code block)
{React.cloneElement(this.props.component, {
open: this.props.open(this.props.name),
close: this.props.close,
})}
</div>
)
}
}
// This is the parent component, using the creator and some state to open/close different components
export class DynamicContentGrid extends React.Component {
constructor() {
super();
this.state = { activeComponent: '' };
}
close() {
this.setState({ activeComponent: '' });
}
open(component) {
this.setState({ activeComponent: component })
}
render() {
console.log(this.props.children);
return(
<div className={css(styles.grid)}>
<div className={css(styles.boxUpperLeft, styles.box)}>
<CreateComponent
component={this.props.children['upperLeft']}
name='upperLeft'
open={() => (name) => this.open(name)}
close={() => this.close()}
/>
</div>
</div>
)
}
}
export default DynamicContentGrid;
And here comes the very basic child component using this.props.close without parameters (they should be set in the creator):
import React from 'react';
export class ImageBox extends React.Component {
render() {
const {title, link, img} = this.props.content.front;
return(
<div>
<h1>{title}</h1>
<h2 onClick={this.props.open}>{link}</h2>
<img src={img} />
</div>
)
}
}
export default ImageBox;
What works
The dynamic rendering of child components works fine.
Where it breaks
As you can see, the magic happens in open={() => (name) => this.open(name)}. What I want is: pass this.open to the creator, set open(name) as parameter and pass on the open function to the child.
Everything works fine, if I said the "name" parameter directly in the parent, but for several reasons I do not want to do this. So I need some kind of currying but I can't figure out, what is wrong. The parameter "name" is not properly set in the creator at the moment.
In CreateComponent set open: () => this.props.open(this.props.name).
Also, remove () => (name) => this.open(name) and replace with this.open and put this.open = this.open.bind(this); into the constructor.
I'm currently creating a component in react and i'm using the ES Lint rule react/jsx-no-bind. My issue here is that I want to be able to pass a parameter to my components function. Here is the code I would like to use to be able to do so:
class LanguageDropdown extends Component {
constructor(props) {
super(props);
this.state = {};
}
changeLanguage = (lang) => {
console.log(lang)
};
render() {
return (
<div>
{this.props.languages.map(lang => <button onCLick={() => this.changeLanguage(lang)}>{lang}</button>)}
</div>
)
}
...
This pulls up the ESlint error:
JSX props should not use arrow functions
I'm not entirely sure how to achieve this without using an arrow function or using .bind(). I could add a data-attribute to the button element and then just pass in the event into the changeLanguage function and fetch the attribute using event.target() but this doesn't feel like it's the way it should be approached in React.
Can someone tell me what would be the correct way?
You can refactor button into its own component:
class MyButton extends Component {
static propTypes = {
language: PropTypes.string.isRequired,
};
onClick = () => console.log(this.props.language);
render() {
const {language} = this.props;
return (
<button onClick={this.onClick} type="submit">
{language}
</button>);
}
}
and then in your LanguageDropDown class, use MyButton like this:
class LanguageDropdown extends Component {
...
render() {
return (
<div>
{this.props.languages.map(lang => <MyButton key={lang} language={lang}/>)}
</div>
)
}
...
}
A couple of additional things:
You have a typo onCLick should be onClick
You need a key for repeated items
try the below code.
here I tried by taking the value into the state, same can be tried using props.
class LanguageDropdown extends Component {
constructor(props) {
super(props);
this.state = {languages:['telugu','hindi','english']};
// this.changeLanguage = this.changeLanguage.bind(this);
}
changeLanguage(event,lang){
//event.preventDefault();
console.log('change lang: '+JSON.stringify(lang));
};
render() {
return (
<div>
{this.state.languages.map(lang => <button onClick={(event)=>this.changeLanguage(event,lang)}>{lang}</button>)}
</div>
)
}
}
render(<LanguageDropdown />, document.getElementById('root'));
when you bind the handler in the onClick event where you are passing the value to the handler, then we have to pass that value from the event and collect it to get that value.
I've hit a brick wall here, meaning I can't fully figure out why the next two versions of code behave so differently.
In the first version, when I'm initialising a this.childComponent = (<ChildComp />), its props do not seem to update when I change the Parent's state (via setState()). This happens even though the setState() is actually called, and the Parent's state is updated.
In the second version, when I'm actually initialising a function that returns the component (this.childComponent = () => {return (<ChildComp />)}), everything works like a charm, props are updated.
I am using the second version (since it works), but I'd like to understand why this works and the first one doesn't.
Here's the child component:
class Child extends React.Component {
render() {
return (
<button onClick=(this.props.setValue())>
{this.props.value}
</button>
)
}
}
I have the next two versions of the parent component:
1.
class Parent extends React.Component {
constructor() {
this.state = {
value: 1
}
this.childComponent = (
<Child value={this.state.value}
setValue={() => this.setValue()}/>
)
}
setValue() {
this.setState({value: 2})
}
render () {
return ( {this.childComponent} )
}
}
2. (the this.childComponent is now a function that returns the react Element)
class Parent extends React.Component {
constructor() {
this.state = {
value: 1
}
this.childComponent = () => {
return (
<Child value={this.state.value}
setValue={() => this.setValue()}/>
)
}
}
setValue() {
this.setState({value: 2})
}
render () {
return ( {this.childComponent()} )
}
}
I've tried to simplify the code so my issue is easier to understand.
Thank you in advance
React uses a strategy called reconciliation to efficiently update the DOM every time there's a change in its internal state. Typically, this happens after a setState call.
In your first example, the render method inside the Parent component always returns the same Child component, as it's created only once in the constructor. Because of this, the reconciliation algorithm doesn't find any changes since there aren't any.
I'd like to point out that <Child value={this.state.value} setValue={() => this.setValue()}/> is just syntactic sugar for React.createElement(Child, {value: this.state.value, setValue: () => this.setValue()}, null). createElement simply returns an object.
In your second example, with every render call, you're calling childComponent which in turns create a new Child component.
You don't have a return in the first case since
this.childComponent = (
<Child value={this.state.value}
setValue={() => this.setValue()}/>
)
was defined in the constructor which is executed only once and is a static value now.
whereas it will work as it is a function that is executed everytime it is called.
If you want to go by the first method, define the childcomponent in the render rather than the constructor since render is called on every change. Your code also had a lot of mistakes. See the working snippet below
class Parent extends React.Component {
constructor() {
super();
this.state = {
value: 1
}
}
setValue() {
this.setState({value: 2})
}
render () {
const childComponent = (
<Child value={this.state.value}
setValue={() => this.setValue()}/>
)
return ( <div>{childComponent}</div> )
}
}
class Child extends React.Component {
render() {
return (
<button onClick={this.props.setValue}>
{this.props.value}
</button>
)
}
}
ReactDOM.render(<Parent/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
I have a component like this:
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props, context) {
super(props, context);
this.state = {
isActive: false,
}
}
showMyComponent() {
this.setState({
isActive: true,
});
}
hideMyComponent() {
this.setState({
isActive: false,
});
}
render() {
return (
<div>
<h1>Compoent Here</h1>
</div>
);
}
}
export default MyComponent;
Now, on my index.js I am adding several components.
...
<Header />
<Nave />
Can I now do something like this here:
MyComponent.showMyComponent();
Like you normally call a function?
If not, how is this done?
You can use references. In your render() method you can get the ref. e.g.
<MyComponent ref={ref => {this.myComponent = ref}}/>
You need to create a field myComponent and assign it to it. With that you can call it like this.myComponent.showMyComponent()
See here Refs and the DOM
Use State
You are thinking about react wrong. You should not have to call a components function like this ever.
You can pass a prop to the component that will make the component hide or show.
or wrap the component in a if in the parent. Use the parents state to hide or show the component.
Like
if (someCondition) {
<MyComponent />
}
It's doable, even if some people hates this option, cause it's not the official React way, true.
You can define any public method on your component classes (such as a reset method on a Typeahead) and call those public methods through refs (such as this.refs.myTypeahead.reset()). In most cases, it's clearer to use the built-in React data flow instead of using refs imperatively.
But However, thinking out of the box, is not forbidden so you can use refs for this.
class Parent extends Component {
onSomeThing() {
// Call some method of myChild
this.myChild.myChildsPublicMethod()
}
render() {
return <MyChild ref={ref => { this.myChild = ref; }} />
}
}
// MyChild
// Just as demo using Pure components here.
// You could use the normal class notation..
const MyChild = () => <div>Ola</div>;
MyChild.someMethod = () => console.log('Ola');
More here https://zhenyong.github.io/react/docs/more-about-refs.html
I encapsulated some HTML code to a extra class and want to hand over a function to it now.
The parent class looks like this:
class Home extends React.Component {
doSomething = id => {
console.log(id);
// here are some fetch operations only available in Home component
};
render() {
return (
<Child doSomething={() => this.doSomething} />
)
}
}
my Child component then looks like this:
const id = 3;
const Child = ({doSomething}) =>
(
<Button onClick={doSomething(id)}>Click</Button>
);
export default Child
I was trying around with different solutions but either I get no result or the onClick function is called when the Home component is rendered instead of when clicking on the button.
I want the function to be executed when the button is clicked. And the id parameter should be handed over as well. I can't have the function in the Child component itself since I have to use some redux actions in it which are not available in the child class.
I know this is not a too difficult question but I'm still a noob with JavaScript..
Edit: I accomplished having the event parameter inside my function but I wonder how to access the id with it. I can't simply add a prop to the Button element since it does not allow that.
Thanks in advance for your help,
Erik
You need to bind method in constructor and pass it to child component
class Home extends React.Component {
constructor() {
this.doSomething = this.doSomething.bind(this);
}
doSomething(id) {
console.log(id);
// here are some fetch operations only available in Home component
}
render() {
return <Child doSomething={this.doSomething} />
}
}
And in Child
const Child = ({doSomething}) =>
(
<Button onClick={() => doSomething(id)}>Click</Button>
)
I think you need something like this:
this.doSomething.bind(this);
It binds this as the first argument of your function, which is needed when you pass a class method as a reference. When doSomething is called in the child component, this will reference the parent component.
First of all your jsx is wrong. Your are missing
render() {
return ...;
}
There is no valid JSX Button
<Button onClick={doSomething(id)}>Click</Button>,
use <button> tag instead.
Here is working example.
const element = <h1>Hello, world</h1>;
class Home extends React.Component {
doSomething = id => {
console.log(id);
// here are some fetch operations only available in Home component
};
render() {
return <Child doSomething={() => this.doSomething('do something input')} />;
}
}
class Child extends React.Component {
constructor(props) {
super(props);
//console.log(props);
}
render() {
return <button onClick={this.props.doSomething.bind(this)}>Click</button>;
}
}
ReactDOM.render(
<Home />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react-dom.js"></script>
<div id="root"></div>
You probably need to pass the id when you are defining the fat arrow function
class Home extends React.Component {
doSomething(id) {
console.log(id);
}
render() {
return <Child doSomething={(id) => this.doSomething(id)} />
}
}
Firstly, in Home component use arrow function as doSomething prop to preserve correct context (alternatively you can use bind in component constructor):
class Home extends React.Component {
doSomething(id) {
console.log(id);
// here are some fetch operations only available in Home component
}
render() {
<Child doSomething={() => this.doSomething()} />
}
}
and then use arrow function that will call passed function with given value as click handler in child component:
<Button onClick={() => doSomething(id)}>Click</Button>