Focusing div elements with React - javascript

Is it possible to focus div (or any other elements) using the focus() method?
I've set a tabIndex to a div element:
<div ref="dropdown" tabIndex="1"></div>
And I can see it gets focused when I click on it, however, I'm trying to dynamically focus the element like this:
setActive(state) {
ReactDOM.findDOMNode(this.refs.dropdown).focus();
}
Or like this:
this.refs.dropdown.focus();
But the component doesn't get focus when the event is triggered. How can I do this? Is there any other (not input) element I can use for this?
EDIT:
Well, It seems this it actually possible to do: https://jsfiddle.net/69z2wepo/54201/
But it is not working for me, this is my full code:
class ColorPicker extends React.Component {
constructor(props) {
super(props);
this.state = {
active: false,
value: ""
};
}
selectItem(color) {
this.setState({ value: color, active: false });
}
setActive(state) {
this.setState({ active: state });
this.refs.dropdown.focus();
}
render() {
const { colors, styles, inputName } = this.props;
const pickerClasses = classNames('colorpicker-dropdown', { 'active': this.state.active });
const colorFields = colors.map((color, index) => {
const colorClasses = classNames('colorpicker-item', [`colorpicker-item-${color}`]);
return (
<div onClick={() => { this.selectItem(color) }} key={index} className="colorpicker-item-container">
<div className={colorClasses}></div>
</div>
);
});
return (
<div className="colorpicker">
<input type="text" className={styles} name={inputName} ref="component" value={this.state.value} onFocus={() => { this.setActive(true) }} />
<div onBlur={() => this.setActive(false) } onFocus={() => console.log('focus')} tabIndex="1" ref="dropdown" className={pickerClasses}>
{colorFields}
</div>
</div>
);
}
}

React redraws the component every time you set the state, meaning that the component loses focus. In this kind of instances it is convenient to use the componentDidUpdate or componentDidMount methods if you want to focus the element based on a prop, or state element.
Keep in mind that as per React Lifecycle documentation, componentDidMount will only happen after rendering the component for the first time on the screen, and in this call componentDidUpdate will not occur, then for each new setState, forceUpdate call or the component receiving new props the componentDidUpdate call will occur.
componentDidMount() {
this.focusDiv();
},
componentDidUpdate() {
if(this.state.active)
this.focusDiv();
},
focusDiv() {
ReactDOM.findDOMNode(this.refs.theDiv).focus();
}
Here is a JS fiddle you can play around with.

This is the problem:
this.setState({ active: state });
this.refs.component.focus();
Set state is rerendering your component and the call is asynchronous, so you are focusing, it's just immediately rerendering after it focuses and you lose focus. Try using the setState callback
this.setState({ active: state }, () => {
this.refs.component.focus();
});

A little late to answer but the reason why your event handler is not working is probably because you are not binding your functions and so 'this' used inside the function would be undefined when you pass it as eg: "this.selectItem(color)"
In the constructor do:
this.selectItem = this.selectItem.bind(this)
this.setActive = this.setActive.bind(this)

This worked in my case
render: function(){
if(this.props.edit){
setTimeout(()=>{ this.divElement.focus() },0)
}
return <div ref={ divElement => this.divElement = divElement}
contentEditable={props.edit}/>
}

Related

React can't figure out how to send data to my parent accordingly

I want to create this React Vending Machine and I need to send some id values from a child to the parent and then make some functions that require that value
I've managed to make the proper function but it doesn't behave like I want to. When I click the button the first time my query is updated but it is not transferred to the parent component. Only when I click the second time my parent gets updated with the state that it was before. So its always one state late.
class App extends Component {
constructor() {
super()
this.handleData = this.handleData.bind(this);
this.state = {
fromChild: ""
}
}
handleData(data) {
this.setState({
fromChild: data
});
}
render() {
return (
<div className="App">
<Glass />
<FrontPanel handlerFromParent={this.handleData.bind(this)}/>
<h5>Received by parent:<br />{this.state.fromChild}</h5>
</div>
);
}
}
class FrontPanel extends Component {
constructor(){
super()
this.state = {
query: ""
}
}
/* function that manages the keybord */
addToQuery = (id) => {
this.setState((prevState) => ({
query: prevState.query + id
}))
this.onChangeQuery();
}
/* function that sends data to parent */
onChangeQuery() {
this.props.handlerFromParent(this.state.query)
}
render() {
return (
<div>
<Screen
credit={this.state.credit}
query={this.state.query}/>
<div className="keybord-layout">
<div className="keybord">
<button onClick={e => this.addToQuery(e.target.id)} id="1">1</button>
<button onClick={e => this.addToQuery(e.target.id)} id="2">2</button>
<button onClick={e => this.addToQuery(e.target.id)} id="3">3</button>
</div>
</div>
</div>
)
}
}
export default FrontPanel;
I want when I click a button to transfer the state to the parent immediately and not wait for another button press. Also, I don't want the state to be behind in the parent.
A setState call is asynchronous so this.state.query may not yet be updated when you called this.onChangeQuery(); that calls your parent component handler.
Try calling your parent handler in the callback function that you can pass to setState.
this.setState(
prevState => ({
query: prevState.query + id
}),
() => this.onChangeQuery()
);
This way, you're ensured that query is the new value when you pass it back to parent.

Save textarea value to div with ReactJs

I'm only trying to deal with the React, so my question may seem very simple, but still I'm stuck on it.
I have two blocks (div.user-data__row) in which there are some values. I change the state of the component (handleChange function), the state in which these blocks are replaced by text fields (textarea.text), and I want when I click on the save button and call saveChange function, the value from each text field is taken and passed to the blocks (1st textarea to 1st block, etc).
I found examples of solving a similar case using the ref attribute, but later read that this is no longer an actual solution and so no one does. Please help me find the actual implementation path.
class UserData extends React.Component {
constructor(props) {
super(props);
this.state = {
edit: true,
};
this.handleChange = this.handleChange.bind(this);
this.saveChange = this.handleChange.bind(this);
}
handleChange() {
this.setState(() => ({
edit: !this.state.edit,
}));
}
saveChange() {
this.setState(() => ({
edit: false
}))
}
render() {
if (this.state.edit) {
return (
<div className="user-data">
<div className="user-data__row">{this.props.userData.name}</div>
<div className="user-data__row">{this.props.userData.email}</div>
<button onClick={ this.handleChange }>Edit</button>
</div>
);
} else {
return (
<div className="user-data">
<textarea className="text" defaultValue={this.props.userData.name}></textarea>
<textarea className="text" defaultValue={this.props.userData.email}></textarea>
<button onClick={ this.saveChange }>Save</button>
</div>
)
}
}
}
Because props are read-only & because userData (email+name) can be changed inside the component , you have to populate props values in the state, then manage the state after that will be enough.
Also , you will need to convert your textarea from uncontrolled component to controlled component by:
Using value instead of defaultValue
Implementing onChange with setState of that value as handler .
Value of textarea should be read from state not props.
If props of <UserData /> may be updated from outside throughout its lifecycle , you will need componentWillReceiveProps later on.
Also you have a typo if (!this.state.edit) { and not if (this.state.edit) { .
class UserData extends React.Component {
state = {
edit: true,
userDataName: this.props.userData.name, // Populate props values
userDataEmail: this.props.userData.email, // Populate props values
};
handleChange = () => {
this.setState((state) => ({
edit: !state.edit,
}));
}
saveChange =() => {
this.setState(() => ({
edit: false
}))
}
render() {
if (!this.state.edit) {
return (
<div className="user-data">
<div className="user-data__row">{this.state.userDataName}</div>
<div className="user-data__row">{this.state.userDataEmail}</div>
<button onClick={ this.handleChange }>Edit</button>
</div>
);
} else {
return (
<div className="user-data">
<textarea className="text" value={this.state.userDataName} onChange={(e) => this.setState({userDataName: e.target.value})}></textarea>
<textarea className="text" value={this.state.userDataEmail} onChange={(e) => this.setState({userDataEmail: e.target.value})}></textarea>
<button onClick={ this.saveChange }>Save</button>
</div>
)
}
}
}
ReactDOM.render(<UserData userData={{name: 'Abdennoor', email: 'abc#mail.com'}} /> , document.querySelector('.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 class="app" />
You're receiving values for <div> from this.props, it means that these values should came from some external source and passed to your component. A way of passing these values to component's props is out of scope of this question, it can be implemented in a very different ways. Usually it came either from parent component or from some connected store.
You need to obtain values from your <textarea> form fields, it can be done directly (using ref) or by using some third-party library that provides form handling. Then these values needs to be stored (and obtained) either directly from component's state or from external source (via props).
Unfortunately scope of your question is too broad to be able to give more precise answer, but hope that this information will lead you to some kind of solution.
You can also use contentEditable, which it will allow you to edit the content of the div.
class UserData extends React.Component {
constructor(props) {
super(props);
this.state = {
edit: true
};
this.handleChange = this.handleChange.bind(this);
}
handleChange() {
this.setState(() => ({
edit: !this.state.edit
}));
}
render() {
const { edit } = this.state;
return (
<div className="user-data">
<div className="user-data__row" contentEditable={edit}>{this.props.userData.name}</div>
<div className="user-data__row" contentEditable={edit}>{this.props.userData.email}</div>
<button onClick={this.handleChange}>
{edit ? Save : Edit}
</button>
</div>
)
}
}

setState being called too late

I am trying to make a text box auto focus.
However, I the setState is being called too late it seems.
It is being called within Popup.show. I created a button to console.log the state, and it does seem to be set to true but it must happen too late.
How can I get setState to be called as the Popup.show happens?
class Dashboard extends React.Component {
constructor(props) {
super(props);
this.state = {
focused: false,
};
}
onClick = (event) => {
console.log('Says focussed FALSE', this.state.focused)
this.setState({ focused:true });
Popup.show(<div>
<SearchBar
autoFocus
focused={this.state.focused}
/>
<button onClick={this.checkState}>It says TRUE here</button>
</div>,
console.log('Says focussed FALSE still', this.state.focused),
{ animationType: 'slide-up'});
};
checkState = (e) =>{
console.log(this.state)
}
render() {
return (
<div style={{ padding: '0.15rem' }}>
<Button onClick={this.onClick.bind(this)}>Open & Focus</Button>
</div>);
}
}
Always remember that setState won't execute immediately. If you want Popup.show() after setState, you can use a callback:
this.setState({ focused: true }, () => {
Popup.show(...)
})
And you are already using arrow functions, you don't need the .bind(this) in your render function.
setState doesn't immediate set the state
From: https://facebook.github.io/react/docs/react-component.html#setstate
Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.
Changing your setState to something like
this.setState({ focused: true }, () => {
Popup.show(<div>
<SearchBar
autoFocus
focused={this.state.focused}
/>
<button onClick={this.checkState}>It says TRUE here</button>
</div>)
});

Change img on hover React re-renders all children

I need to change the url of the img on hover.
But the function trigger all of the children thats render.
I couldn't find a way to make the function trigger each children separate.
I try to make some new state to handle the indexes, but it didn't work...
const Team = React.createClass ({
getInitialState : function() {
return { hovered: false }
},
componentDidMount(){
this.props.fetchTeam();
document.title = "Equipe | [ Adesign";
},
onMouseOver : function () {
this.setState({ hovered:true });
},
onMouseOut : function () {
this.setState({ hovered:false });
},
render(){
return (
<div className="wrapper">
<div className="Team">
<HeaderAbout />
<h1>EQUIPE</h1>
<div className="content-team">
{this.props.posts.team.map((singleTeam, i) => {
var imgUrl = singleTeam.acf.photo.url;
if (this.state.hovered) {
imgUrl = singleTeam.acf.gif.url;
} else {
imgUrl = singleTeam.acf.photo.url;
}
return(
<div key={i} className="single-team col-xs-12 col-sm-6 col-md-4" onMouseOver={this.onMouseOver} onMouseOut={this.onMouseOut}>
<img className="img-responsive" src={imgUrl}/>
<p>{singleTeam.title.rendered}</p>
</div>
)
})}
</div>
</div>
</div>
)
}
});
You want to checkout the shouldComponentUpdate method:
Invoked before rendering when new props or state are being received.
This method is not called for the initial render or when forceUpdate
is used.
Use this as an opportunity to return false when you're certain that
the transition to the new props and state will not require a component
update.
In order to avoid rerendering all the images you can create a component to render an image that contains the state and the event handlers. By doing so, you prevent to rerender the parent component and its siblings whenever an image is hovered.
Edit: I just realized that your code changes all the images when any of them is hovered. Are you sure that it is what you want? In that case, you need to rerender everything. My solution is only valid if you only want to change the hovered image, leaving the others intact.

setState() inside of componentDidUpdate()

I'm writing a script which moves dropdown below or above input depending on height of dropdown and position of the input on the screen. Also I want to set modifier to dropdown according to its direction.
But using setState inside of the componentDidUpdate creates an infinite loop(which is obvious)
I've found a solution in using getDOMNode and setting classname to dropdown directly, but i feel that there should be a better solution using React tools. Can anybody help me?
Here is a part of working code with getDOMNode (i
a little bit neglected positioning logic to simplify code)
let SearchDropdown = React.createClass({
componentDidUpdate(params) {
let el = this.getDOMNode();
el.classList.remove('dropDown-top');
if(needToMoveOnTop(el)) {
el.top = newTopValue;
el.right = newRightValue;
el.classList.add('dropDown-top');
}
},
render() {
let dataFeed = this.props.dataFeed;
return (
<DropDown >
{dataFeed.map((data, i) => {
return (<DropDownRow key={response.symbol} data={data}/>);
})}
</DropDown>
);
}
});
and here is code with setstate (which creates an infinite loop)
let SearchDropdown = React.createClass({
getInitialState() {
return {
top: false
};
},
componentDidUpdate(params) {
let el = this.getDOMNode();
if (this.state.top) {
this.setState({top: false});
}
if(needToMoveOnTop(el)) {
el.top = newTopValue;
el.right = newRightValue;
if (!this.state.top) {
this.setState({top: true});
}
}
},
render() {
let dataFeed = this.props.dataFeed;
let class = cx({'dropDown-top' : this.state.top});
return (
<DropDown className={class} >
{dataFeed.map((data, i) => {
return (<DropDownRow key={response.symbol} data={data}/>);
})}
</DropDown>
);
}
});
You can use setStateinside componentDidUpdate. The problem is that somehow you are creating an infinite loop because there's no break condition.
Based on the fact that you need values that are provided by the browser once the component is rendered, I think your approach about using componentDidUpdate is correct, it just needs better handling of the condition that triggers the setState.
The componentDidUpdate signature is void::componentDidUpdate(previousProps, previousState). With this you will be able to test which props/state are dirty and call setState accordingly.
Example:
componentDidUpdate(previousProps, previousState) {
if (previousProps.data !== this.props.data) {
this.setState({/*....*/})
}
}
If you use setState inside componentDidUpdate it updates the component, resulting in a call to componentDidUpdate which subsequently calls setState again resulting in the infinite loop. You should conditionally call setState and ensure that the condition violating the call occurs eventually e.g:
componentDidUpdate: function() {
if (condition) {
this.setState({..})
} else {
//do something else
}
}
In case you are only updating the component by sending props to it(it is not being updated by setState, except for the case inside componentDidUpdate), you can call setState inside componentWillReceiveProps instead of componentDidUpdate.
This example will help you to understand the React Life Cycle Hooks.
You can setState in getDerivedStateFromProps method i.e. static and trigger the method after props change in componentDidUpdate.
In componentDidUpdate you will get 3rd param which returns from getSnapshotBeforeUpdate.
You can check this codesandbox link
// Child component
class Child extends React.Component {
// First thing called when component loaded
constructor(props) {
console.log("constructor");
super(props);
this.state = {
value: this.props.value,
color: "green"
};
}
// static method
// dont have access of 'this'
// return object will update the state
static getDerivedStateFromProps(props, state) {
console.log("getDerivedStateFromProps");
return {
value: props.value,
color: props.value % 2 === 0 ? "green" : "red"
};
}
// skip render if return false
shouldComponentUpdate(nextProps, nextState) {
console.log("shouldComponentUpdate");
// return nextState.color !== this.state.color;
return true;
}
// In between before real DOM updates (pre-commit)
// has access of 'this'
// return object will be captured in componentDidUpdate
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("getSnapshotBeforeUpdate");
return { oldValue: prevState.value };
}
// Calls after component updated
// has access of previous state and props with snapshot
// Can call methods here
// setState inside this will cause infinite loop
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("componentDidUpdate: ", prevProps, prevState, snapshot);
}
static getDerivedStateFromError(error) {
console.log("getDerivedStateFromError");
return { hasError: true };
}
componentDidCatch(error, info) {
console.log("componentDidCatch: ", error, info);
}
// After component mount
// Good place to start AJAX call and initial state
componentDidMount() {
console.log("componentDidMount");
this.makeAjaxCall();
}
makeAjaxCall() {
console.log("makeAjaxCall");
}
onClick() {
console.log("state: ", this.state);
}
render() {
return (
<div style={{ border: "1px solid red", padding: "0px 10px 10px 10px" }}>
<p style={{ color: this.state.color }}>Color: {this.state.color}</p>
<button onClick={() => this.onClick()}>{this.props.value}</button>
</div>
);
}
}
// Parent component
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { value: 1 };
this.tick = () => {
this.setState({
date: new Date(),
value: this.state.value + 1
});
};
}
componentDidMount() {
setTimeout(this.tick, 2000);
}
render() {
return (
<div style={{ border: "1px solid blue", padding: "0px 10px 10px 10px" }}>
<p>Parent</p>
<Child value={this.state.value} />
</div>
);
}
}
function App() {
return (
<React.Fragment>
<Parent />
</React.Fragment>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
I would say that you need to check if the state already has the same value you are trying to set. If it's the same, there is no point to set state again for the same value.
Make sure to set your state like this:
let top = newValue /*true or false*/
if(top !== this.state.top){
this.setState({top});
}
this.setState creates an infinite loop when used in ComponentDidUpdate when there is no break condition in the loop.
You can use redux to set a variable true in the if statement and then in the condition set the variable false then it will work.
Something like this.
if(this.props.route.params.resetFields){
this.props.route.params.resetFields = false;
this.setState({broadcastMembersCount: 0,isLinkAttached: false,attachedAffiliatedLink:false,affilatedText: 'add your affiliate link'});
this.resetSelectedContactAndGroups();
this.hideNext = false;
this.initialValue_1 = 140;
this.initialValue_2 = 140;
this.height = 20
}
I faced similar issue. Please make componentDidUpdate an arrow function. That should work.
componentDidUpdate = (params) => {
let el = this.getDOMNode();
if (this.state.top) {
this.setState({top: false});
}
if(needToMoveOnTop(el)) {
el.top = newTopValue;
el.right = newRightValue;
if (!this.state.top) {
this.setState({top: true});
}
}
}
I had a similar problem where i have to center the toolTip. React setState in componentDidUpdate did put me in infinite loop, i tried condition it worked. But i found using in ref callback gave me simpler and clean solution, if you use inline function for ref callback you will face the null problem for every component update. So use function reference in ref callback and set the state there, which will initiate the re-render
You can use setState inside componentDidUpdate

Categories