In my app.js for the button component I gave this props propTwo={"two"}
but still Button.js nothing is printing inside componentWillReceiveProps
can yoi tell me how to recieve the props using componentWillReceiveProps method
can you tell me how to use componentWillReceiveProps in my application
providing my code snippet and sandbox below
https://codesandbox.io/s/hopeful-villani-xyikq
class Button extends Component {
componentWillReceiveProps(nextprops) {
console.log("componentWillReceiveProps nextprops--->", nextprops);
}
render() {
return (
<div>
<button
onClick={() => {
// getPosts(channel);
// getAlert();
}}
className="btn btn-primary btn-lg btn-block"
>
Get top news
</button>
</div>
);
}
}
App.js
const App = () => (
<div>
<RecentChannelItem />
<ChannelsField propOne={"one"} />
<Button propTwo={"two"} />
<TopNews />
</div>
);
componentWillReceiveProps is already deprecated.
You can use componentDidUpdate.
You can access the updated props by accessing this.props inside the componentDidUpdate.
You should use:
static getDerivedStateFromProps(nextprops) {
console.log("componentWillReceiveProps nextprops--->", nextprops);
}
Related
React newbie here,
I have a component for login which has two states (logged and not), and a button, a button click makes a form appear for the user.
function Main() {
const [state, setState] = useState(false);
function buttonClick(event) {
state = !state;
}
return (
<div id='main'>
<p id='main-body'>{lorem}</p>
{ state ? <LoginForm /> : <LoginButton />}
</div>
)
}
my issue is that the onClick itself (and the button) are in another component:
function LoginButton() {
return (
<div class="btn-centered">
<button type="button" class="btn btn-outline-primary btn-lg btn-centered" onClick={}>Login</button>
</div>
)
}
Have I understood the concept wrongly? How do I achieve the result I want with the outside component?
Thanks a lot for any help, I'd VERY appreciate explanations so I can learn.
You can pass buttonClick method in login button component like this
<LoginButton onClick={buttonClick} />
and inside Loginbutton component you can do something like this
<button type="button" ... onClick={props.onClick}>Login</button>
so when you click login button it will trigger you buttonClick method.
the LoginButton should get props and return a callback to the Main that will switch the state and you should also use useState to change the state in order to change the DOM,
thats how it should look
function Main() {
const [state, setState] = useState(false);
function buttonClick(event) {
setState((prev) => !prev);
}
return (
<div id='main'>
<p id='main-body'>{lorem}</p>
{ state ? <LoginForm /> : <LoginButton callback={buttonClick} />}
</div>
)
function LoginButton(props) {
const {callback} = props;
return (
<div class="btn-centered">
<button type="button" class="btn btn-outline-primary btn-lg btn-centered" onClick={callback}>Login</button>
</div>
)
}
I'm currently looking for a way to access children state from a parent component that will handle API calls for the whole page.
The actual problem is the following:
Parent is the parent component that will render two Child components.
Each of the Child has a state that it is responsible for.
The "Kind of Submit Button" will have a "Kind of Submmit Action" (this is all quoted because this is not a form) and that should fire the function to provide access to the children state. Is there a way (some React feature) to do this without using <form> or without creating an intermediate parent component to hold all the state? I want each children to be responsible for its own state.
Code Sandbox with example of the code below
import React, { useState, useRef } from "react";
function ChildOne() {
const [childOneState, setChildOneState] = useState(false);
return (
<React.Fragment>
<h3>Child One</h3>
<p>My state is: {childOneState.toString()}</p>
<button onClick={() => setChildOneState(true)}>Change my state</button>
</React.Fragment>
);
}
function ChildTwo() {
const [childTwoState, setChildTwoState] = useState(false);
return (
<React.Fragment>
<h3>Child Two</h3>
<p>My state is: {childTwoState.toString()}</p>
<button onClick={() => setChildTwoState(true)}>Change my state</button>
</React.Fragment>
);
}
function Button(props) {
return (
<button onClick={props.kindOfSubmitAction}>Kind of Submit Button</button>
);
}
function Parent() {
const childOneState = useRef("i have no idea");
const childTwoState = useRef("ihave no idea");
function kindOfSubmitAction() {
console.log("This is the kindOfSubmit function!");
// This function would somehow get
// access to the children state and store them into the refs
return;
}
return (
<React.Fragment>
<h1>Iam Parent</h1>
<div>
<b>Child one state is: </b>
{childOneState.current}
</div>
<div>
<b>Child two state is: </b>
{childTwoState.current}{" "}
</div>
<Button kindOfSubmitAction={kindOfSubmitAction} />
<ChildOne />
<ChildTwo />
</React.Fragment>
);
}
export default Parent;
When several components need access to the same data, it's time for Lifting State Up.
I have a simple React component that renders multiple buttons from an array in my props. I'm applying the ripple on DidMount, however, it's only attaching on the first button, the rest are being ignored. It looks like the attachTo only takes the first element. Is there another way to attach to all the buttons on didmount?
class NavBar extends Component {
constructor(props) {
super(props);
this.state = {
links
};
}
componentDidMount() {
MDCRipple.attachTo(document.querySelector('.mdc-button'));
}
render() {
return (
<section>
{this.state.links.map((link, i) => {
return (
<StyledLink key={i} to={link.url}>
<StyledButton className="mdc-button">
<StyledIcon className="material-icons">{link.icon}</StyledIcon>
<StyledTypography className="mdc-typography--caption">
{link.title}
</StyledTypography>
</StyledButton>
</StyledLink>
);
})}
</section>
);
}
}
Final markup
<a class="sc-iwsKbI bhaIR">
<button class="mdc-button sc-dnqmqq ksXmjj mdc-ripple-upgraded" style="--mdc-ripple-fg-size:57.599999999999994px; --mdc-ripple-fg-scale:2.1766951530355496; --mdc-ripple-fg-translate-start:-7.799999999999997px, 19.200000000000003px; --mdc-ripple-fg-translate-end:3.200000000000003px, 19.200000000000003px;">
...content
</button>
</a>
<a class="sc-iwsKbI bhaIR">
<button class="mdc-button sc-dnqmqq ksXmjj">
...content
</button>
</a>
Updated
I was able to find a way to use the attachTo with each button, but it still seems like there's a better way.
I changed by componentDidMount() to:
componentDidMount() {
this.state.links.forEach((link) => {
MDCRipple.attachTo(document.getElementById(`button-navbar-${link.id}`));
});
}
and then changed my render to
<StyledButton id={`button-navbar-${link.id}`} className="mdc-button">
Is there a way to do this without having to iterate through the array?
The react way to do this is to write component that injects the necessary logic.
class RippleButton extends Component {
const handleRef = elem => MDCRipple.attachTo(elem);
render() {
return (
<StyledButton {...this.props} ref={this.handleRef} />
);
}
}
Then render that component instead of your original StyledButton component and it will call the MDCRipple.attachTo() itself with its ref.
Depending on how the StyledButton is implemented you may need to use another prop to get the ref to the underlying DOM element. You did not provide enough of your code to exactly know this.
I have created a loading icon component, which simply displays a spinner while loading something. I load it into my Sign In component, and wish to display the icon once the user clicks the Login button (And the API call is busy).
So:
import Loading from '../common/loading';
I then set an isLoading variable, defaulted to false:
this.isLoading = false;
Then, within my render method, I wish to determin if I need to show the spinner or not.
render() {
var LoadingSpinner = this.state.canLogin ? Loading : '<div></div>';
This fails.
And then my button is where I show the spinner. I'm hoping to hide the 'Sign In' text, and replace it with the spinner, but ... first thing is to handle the spinner, based on the isLoading variable.
<button
className="btn btn-lg btn-primary btn-block"
type="button"
onClick={this.handleSignin}
disabled={!this.state.canLogin}>
<span>Sign in</span> <LoadingSpinner />
</button>
</div>
Can/should this be done this way, OR... should I maybe pass a prop to my Loading component, called 'Visible' or something, and I set that?
put isLoading to constructor with default false
and then inside the render method, just add a condition
{ this.state.canLogin ? <LoadingSpinner /> : null }
Here is what you could do, using a state variable.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false
}
}
onClick = () => {
this.setState({
loading: true
})
}
render() {
return (
<div>
{this.state.loading && <div>Loading</div>}
<button onClick={this.onClick}>Click to Load</button>
</div>
);
}
}
ReactDOM.render( < App / > , document.getElementById('root'));
<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='root'>
</div>
I have this component:
import React from 'react';
export default class AddItem extends React.Component {
add() {
this.props.onButtonClick(this.input.value);
this.input.value = '';
}
render() {
return (
<div className="add-item">
<input type="text" className="add-item__input" ref={(input) => this.input = input} placeholder={this.props.placeholder} />
<button disabled={!this.input.value} className="add-item__button" onClick={this.add.bind(this)}>Add</button>
</div>
);
}
}
I want the button to be disabled when input value is empty. But the code above doesn't work. It says:
add-item.component.js:78 Uncaught TypeError: Cannot read property 'value' of undefined
pointing to disabled={!this.input.value}. What can I be doing wrong here? I'm guessing that perhaps ref isn't created yet when render method is executed. If, so what is the workararound?
Using refs is not best practice because it reads the DOM directly, it's better to use React's state instead. Also, your button doesn't change because the component is not re-rendered and stays in its initial state.
You can use setState together with an onChange event listener to render the component again every time the input field changes:
// Input field listens to change, updates React's state and re-renders the component.
<input onChange={e => this.setState({ value: e.target.value })} value={this.state.value} />
// Button is disabled when input state is empty.
<button disabled={!this.state.value} />
Here's a working example:
class AddItem extends React.Component {
constructor() {
super();
this.state = { value: '' };
this.onChange = this.onChange.bind(this);
this.add = this.add.bind(this);
}
add() {
this.props.onButtonClick(this.state.value);
this.setState({ value: '' });
}
onChange(e) {
this.setState({ value: e.target.value });
}
render() {
return (
<div className="add-item">
<input
type="text"
className="add-item__input"
value={this.state.value}
onChange={this.onChange}
placeholder={this.props.placeholder}
/>
<button
disabled={!this.state.value}
className="add-item__button"
onClick={this.add}
>
Add
</button>
</div>
);
}
}
ReactDOM.render(
<AddItem placeholder="Value" onButtonClick={v => console.log(v)} />,
document.getElementById('View')
);
<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='View'></div>
In HTML,
<button disabled/>
<button disabled="true">
<button disabled="false">
<button disabled="21">
All of them boils down to disabled="true" that is because it returns true for a non-empty string.
Hence, in order to return false, pass a empty string in a conditional statement like this.input.value ? "true" : "".
render() {
return (
<div className="add-item">
<input
type="text"
className="add-item__input"
ref={(input) => this.input = input}
placeholder={this.props.placeholder}
/>
<button
disabled={this.input.value ? "true" : ""}
className="add-item__button"
onClick={this.add.bind(this)}
>
Add
</button>
</div>
);
}
Here is a functional component variety using react hooks.
The example code I provided should be generic enough for modification with the specific use-case or for anyone searching "How to disable a button in React" who landed here.
import React, { useState } from "react";
const YourComponent = () => {
const [isDisabled, setDisabled] = useState(false);
const handleSubmit = () => {
console.log('Your button was clicked and is now disabled');
setDisabled(true);
}
return (
<button type="button" onClick={handleSubmit} disabled={isDisabled}>
Submit
</button>
);
}
export default YourComponent;
There are few typical methods how we control components render in React.
But, I haven't used any of these in here, I just used the ref's to namespace underlying children to the component.
class AddItem extends React.Component {
change(e) {
if ("" != e.target.value) {
this.button.disabled = false;
} else {
this.button.disabled = true;
}
}
add(e) {
console.log(this.input.value);
this.input.value = '';
this.button.disabled = true;
}
render() {
return (
<div className="add-item">
<input type="text" className = "add-item__input" ref = {(input) => this.input=input} onChange = {this.change.bind(this)} />
<button className="add-item__button"
onClick= {this.add.bind(this)}
ref={(button) => this.button=button}>Add
</button>
</div>
);
}
}
ReactDOM.render(<AddItem / > , document.getElementById('root'));
<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="root"></div>
You shouldn't be setting the value of the input through refs.
Take a look at the documentation for controlled form components here - https://facebook.github.io/react/docs/forms.html#controlled-components
In a nutshell
<input value={this.state.value} onChange={(e) => this.setState({value: e.target.value})} />
Then you will be able to control the disabled state by using disabled={!this.state.value}
very simple solution for this is by using useRef hook
const buttonRef = useRef();
const disableButton = () =>{
buttonRef.current.disabled = true; // this disables the button
}
<button
className="btn btn-primary mt-2"
ref={buttonRef}
onClick={disableButton}
>
Add
</button>
Similarly you can enable the button by using buttonRef.current.disabled = false
this.input is undefined until the ref callback is called. Try setting this.input to some initial value in your constructor.
From the React docs on refs, emphasis mine:
the callback will be executed immediately after the component is mounted or unmounted
I have had a similar problem, turns out we don't need hooks to do these, we can make an conditional render and it will still work fine.
<Button
type="submit"
disabled={
name === "" || email === "" || password === ""
}
fullWidth
variant="contained"
color="primary"
className={classes.submit}>
SignUP
</Button>