How to set focus on button in componentDidMount - reactjs

i have this component
class XY extends Component {
constructor(props) {
super(props)
this.state = {
open: false
}
}
componentDidMount() {
if(this.state.open === false && this.props.data.active === 1) {
this.button.focus()
}
}
render() {
return (
<div>
<button ref={c => (this.button = c)}>
button
</button>
</div>
)
}
}
I need to set focus on rendered button after component mounts and under some conditions, but it doesn't work for some reason. I tried to set it up in componentDidUpdate and it worked but not on first render obv. Is there anything I've done wrong?
Thanks for help

You need to use refs callback to focus on input button.
class XY extends React.Component{
componentDidMount(){
this.focusInput.focus();
}
render() {
return(
<div>
<input type="button"
ref={(input) => { this.focusInput = input; }}
defaultValue="button"
/>
</div>
);
}
}
ReactDOM.render(<XY />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.js"></script>
<div id="root"></div>

Related

React - State change of parent does not cause re-rendering of child when passed down as props?

I have been trying to learn React lately and this issue has really confused me. Here's the code:
I have created two class components: App, representing the parent and Child, representing the child.
class App extends React.Component {
constructor() {
super()
this.state = {
myState: 0
}
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState((previousState) => ({myState: previousState.myState + 1}), () => console.log(this.state.myState))
}
render() {
return (
<div>
<Child value={this.state.myState}/>
<button onClick={this.handleClick}>This updates the state of App (parent)</button>
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props)
this.state = {
value: this.props.value
}
this.randomize = this.randomize.bind(this)
}
randomize() {
this.setState({value: Math.random() * 100})
}
render() {
return(
<div>
<h1>My value is {this.state.value}</h1>
<button onClick={this.randomize}>This changes the state of Child</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
The App component contains a button which increments its state value by 1 every time it is clicked. The Child component contains a button which changes its state value to a random number between 0 and 100 every time it is clicked.
While running this, I expected the Child to be re-rendered every time myState in the parent changed, since that is what I passed down to it as props. I also expected this.state.value in the Child to change because I set it to be myState. However, when I incremented myState, the Child is completely unaffected and still displays whatever randomize() gave to it before.
Can anyone show me what's wrong? Thanks :)
You have the following snippet in your Child constructor:
this.state = {
value: this.props.value
}
The above only sets the Child state once, before the component is mounted. Therefore, none of your increments/decrements push through from your parent to child.
The best solution to your problem is to simply keep the state in your parent component, and only pass down a function that can update it.
Try this:
class App extends React.Component {
constructor() {
super()
this.state = {
myState: 0
}
this.handleClick = this.handleClick.bind(this)
this.updateState = this.updateState.bind(this)
}
updateState(newState) {
this.setState(newState);
}
handleClick() {
this.setState((previousState) => ({myState: previousState.myState + 1}), () => console.log(this.state.myState))
}
render() {
return (
<div>
<Child value={this.state.myState} update={this.updateState}/>
<button onClick={this.handleClick}>This updates the state of App (parent)</button>
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props)
this.randomize = this.randomize.bind(this)
}
randomize() {
this.props.update({myState: Math.random() * 100})
}
render() {
return(
<div>
<h1>My value is {this.props.value}</h1>
<button onClick={this.randomize}>This changes the state of Child</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
You are missing component lifecycle. constructor of a class called once only, not on every re-render.
If u wants to change the data of child wrt parent, then use componentDidUpdate(), which will get called whenever data is updated.
refer here
Modified Code for child:
class App extends React.Component {
constructor() {
super()
this.state = {
myState: 0
}
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState((previousState) => ({myState: previousState.myState + 1}), () => console.log(this.state.myState))
}
render() {
return (
<div>
<Child value={this.state.myState}/>
<button onClick={this.handleClick}>This updates the state of App (parent)</button>
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props)
this.state = {
value: this.props.value
}
this.randomize = this.randomize.bind(this)
}
randomize() {
this.setState({value: Math.random() * 100})
}
componentDidUpdate(prevProps, prevState) {
if (this.props.value !== prevProps.value) {
this.setState({value: this.props.value + prevState.value - prevProps.value})
}
}
render() {
return(
<div>
<h1>My value is {this.state.value}</h1>
<button onClick={this.randomize}>This changes the state of Child</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>

Passing input value to sibling through parent

I'm trying to pass input value from one child to another child using setState. It renders the first element, but not the second element in the first child...
class App extends Component {
constructor(props) {
super(props);
this.state = {};
};
showTekst = () => {
const inpValue = document.querySelector('.inpValue').value;
this.setState({
name: 'Bruce Wayne',
namefromvalue: inpValue.value
});
}
render() {
return (
<div className="app">
<Button showTekst={this.showTekst.bind(this)} />
<Text name={this.state.name} namefromvalue={this.state.namefromvalue}/>
<Inputvalue />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
class Inputvalue extends Component {
render() {
return (
<input className="inpValue"></input>
);
}
}
class Text extends Component {
render() {
return (
<div>
<h1>This is text..</h1>
<h2>{this.props.namefromvalue}</h2>
<p>{this.props.name}</p>
</div>
);
}
}
If you want to show the text from the Inputvalue component in the Text component only when the Button component is clicked you can store the input value in the App instance and update the App state when the Button is clicked.
To do so, we define an onChange prop in the Inputvalue component so that every time the text in the input changes, the prop is called with the new value. With that prop we update the value in the App instance. This value will always be in sync with the input value.
Last, when the button is clicked we update the App state with the input value so that the Text component renders its value.
class App extends Component {
constructor(props) {
super(props);
this.state = {};
// This variable will hold the input value.
this.inputValue = "";
}
showTekst = () => {
// Update with the current input value.
this.setState({
name: "Bruce Wayne",
namefromvalue: this.inputValue
});
};
onInputChange = value => {
// Update the input value.
this.inputValue = value;
};
render() {
return (
<div className="app">
<Button showTekst={this.showTekst.bind(this)} />
<Text name={this.state.name} namefromvalue={this.state.namefromvalue} />
<Inputvalue onChange={this.onInputChange} />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
class Button extends Component {
render() {
return <button onClick={this.props.showTekst}>Show text</button>;
}
}
class Inputvalue extends Component {
render() {
return (
<input
className="inpValue"
onChange={ev => this.props.onChange(ev.target.value)}
/>
);
}
}
class Text extends Component {
render() {
return (
<div>
<h1>This is text..</h1>
<h2>{this.props.namefromvalue}</h2>
<p>{this.props.name}</p>
</div>
);
}
}

Reset State on Props Change

So, I have three components(Search, Pages, HanddlesApi) plus App.js and I'm passing props around via functions to other child components with no problem.
The Search component passes it's state of userInput to HandleApi component and updates the api using componentDidUpdate. (this works great, np here).
I added the Pages component to update the api's page number so that the user can cycle through pages of content. This works, but with issues. The user can do a search and cycle though the pages, but if they enter a new query, they will land  on the same page number of the new query. For example, If
I searched "ducks" and clicked to the next page(2). Then did a search for "dogs" they would land on page two of "dogs"
So my question is how do I reset state for my Pages component only when a user enters a new query?
I saw that componentWillReceiveProps is being deprecated, so I can't use that.
getDerivedStateFromProps  seemed like it might be a good idea, but from what I read it should only be used in rare cases.
So, the two most likely options seemed to be, use componentDidUpdate in a way I don't understand or use key?
Overall I'm just confused on what to do
In my HanddlesApi Component I'm passing the follwoing into the API:
q: this.props.inputValue ? this.props.inputValue : 'news',
page: this.props.pageNum ? this.props.pageNum: 0
then..
componentDidMount() {
this.fetchNews()
}
componentDidUpdate(prevProps, prevState) {
if (this.props.inputValue !== prevProps.inputValue || this.props.pageNum !== prevProps.pageNum) {
this.setState({
news: []
}, this.fetchNews);
}
}
Then in my Pages Component, I have
import React, { Component } from 'react'
class Pages extends Component {
constructor(props) {
super(props)
this.state = {
nextPage: 1,
prevPage: 0
}
}
handleNextClick = () => {
this.setState({
nextPage: this.state.nextPage + 1,
})
}
handlePrevClick = () => {
this.setState({
prevPage: this.state.prevPage - 1,
})
}
render() {
return (
<div className='pageNav'>
<button className="PrevButton" onClick={() => {
this.handlePrevClick()
this.props.onNextButtonClick(this.state.prevPage)
}}>Previous </button>
<button className="nextButton" onClick={() => {
this.handleNextClick()
this.props.onNextButtonClick(this.state.nextPage)
}}>Next </button>
</div>
)
}
}
export default Pages
Search Component
import React, { Component } from 'react';
class SearchBar extends Component {
constructor(props) {
super(props)
this.state = {
inputValue: ""
}
}
handleChange = (e) => {
this.setState({
inputValue: e.target.value
})
}
handleSubmit = (e) => {
e.preventDefault()
this.props.onSubmittedSearch(this.state.inputValue)
}
render() {
//{this.props.onSubmittedSearch(this.state.inputValue)}
return (
<section>
<form onSubmit={this.handleSubmit}>
<label htmlFor="searching"></label>
<input type="text" placeholder="Search Something" value={this.state.inputValue} onChange={this.handleChange} />
<button type="submit">Search </button>
</form>
</section>
)
}
}
export default SearchBar
App.js
class App extends Component {
constructor(props) {
super(props)
this.state = {
inputValue: null,
pageNum: 1
}
}
// used to pass props from SearchBar to NewsList
onSubmittedSearch = (inputValue) => {
this.setState({
inputValue: inputValue
})
}
onNextButtonClick = (pageNum) => {
this.setState({
pageNum: pageNum
})
}
render() {
return (
<main>
<SearchBar onSubmittedSearch={this.onSubmittedSearch} />
<NewsList inputValue={this.state.inputValue} pageNum={this.state.pageNum} />
<Pages onNextButtonClick={this.onNextButtonClick} />
<Footer />
</main>
)
}
}
export default App;
You should let App in charge of changing and holding the current page number. So you can reset it each time your search component submit. Here is a working exemple:
class Pages extends React.Component {
render() {
return (<div className='pageNav'>
<button disabled={this.props.page <= 1} className="PrevButton" onClick={this.props.onPrevButtonClick}>Previous
</button>
<span>{this.props.page}</span>
<button className="nextButton" onClick={this.props.onNextButtonClick}>Next
</button>
</div>)
}
}
class SearchBar extends React.Component {
constructor(props) {
super(props)
this.state = {
inputValue: ""
}
}
handleChange = (e) => {
this.setState({inputValue: e.target.value})
}
handleSubmit = (e) => {
e.preventDefault()
this.props.onSubmittedSearch(this.state.inputValue)
}
render() {
//{this.props.onSubmittedSearch(this.state.inputValue)}
return (<section>
<form onSubmit={this.handleSubmit}>
<label htmlFor="searching"></label>
<input type="text" placeholder="Search Something" value={this.state.inputValue} onChange={this.handleChange}/>
<button type="submit">Search
</button>
</form>
</section>)
}
}
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
inputValue: null,
pageNum: 1
}
}
// used to pass props from SearchBar to NewsList
onSubmittedSearch = (inputValue) => {
this.setState({inputValue: inputValue, pageNum: 1})
}
onNextButtonClick = () => {
this.setState(state => ({
pageNum: state.pageNum + 1
}))
}
onPrevButtonClick = (pageNum) => {
this.setState(state => ({
pageNum: Math.max(state.pageNum - 1, 1)
}))
}
render() {
return (<main>
<SearchBar onSubmittedSearch={this.onSubmittedSearch}/>
<Pages onNextButtonClick={this.onNextButtonClick} onPrevButtonClick={this.onPrevButtonClick} page={this.state.pageNum}/>
</main>)
}
}
ReactDOM.render(<App/>, document.getElementById('root'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>

React: Open Modal from a different component

I have a Modal component in the Main.js app and I want to trigger it from a different component (in this case Homepage, but I have one component for each page).
I don´t know how to pass a component to be rendered inside the modal.
If it helps I´m using Context API.
App.js
const App = () => {
return (
<div>this is the main app</div>
<Modal />
)
}
ReactDOM.render(<App />, document.getElementById('root'))
Modal.js
class Modal extends Component {
constructor(props) {
super(props)
this.state = {
'open': false
}
}
componentWillReceiveProps(nextProps) {
this.setState({open: nextProps.open})
}
render() {
if (!this.state.open) {
return false
}
return(
<div className="modal">
{this.props.children}
</div>
)
}
}
export default Modal
Homepage.js
class Homepage extends Component {
constructor(props) {
super(props)
this.handleOpenModal = this.handleOpenModal.bind(this)
}
handleOpenModal() {
// here I want to open the modal and pass the <ModalContent /> component
// basically call the <Modal open="true"> from the Main component
}
render() {
return(
<div className="homepage">
<button onClick={this.handleOpenModal}>open the modal</button>
</div>
)
}
}
const ModalContent = () => {
return(
<div>this is the content I want to render inside the modal</div>
)
}
thank you.
I strongly recommend using something like react-modal (npm). It allows you to keep modal content right next to the trigger. It does this by appending a "portal" high up in the DOM and handles appending the content to is.
Your example may look like the following:
import Modal from 'react-modal';
class Homepage extends Component {
constructor(props) {
super(props)
this.state = { modalOpen: false };
this.handleOpenModal = this.handleOpenModal.bind(this)
}
handleOpenModal() {
this.setState({ modalOpen: true });
}
render() {
return(
<div className="homepage">
<button onClick={this.handleOpenModal}>open the modal</button>
<Modal open={this.state.modalOpen}>
<ModalContent />
</Modal>
</div>
)
}
}
const ModalContent = () => {
return(
<div>this is the content I want to render inside the modal</div>
)
}

What is the right way to add onClick event on element after render?

I'd like to add onClick event only on an element of the list(array)
class App extends React.Component {
constructor(){
super();
this.state = {
list: ['Value1','Value2']
}
}
render(){
return (
<div className="App">
{this.state.list.map(data => {
return data
})} // How can I add onClick event only on 'Value1' after it rendered?
</div>
);
}
}
My solution is:
const [Value1,Value2] = this.state.list
value1.addEventListener('click',function(){})
but it seems like doesn't work.
In React, you can bind onClick in the component. You can read more about event handling in here: https://reactjs.org/docs/handling-events.html
Your code can be changed into this:
class App extends React.Component {
constructor() {
super()
this.state = {
list: ['Value1', 'Value2']
}
}
handleClick = e => {
console.log('You clicked the item!!')
}
render() {
return (
<div className="App">
{this.state.list.map(data => {
return (
<span onClick={data === 'Value1' ? this.handleClick : undefined}>
{data}
</span>
)
})}
</div>
)
}
}

Resources