import React from 'react'
import Input as InputAnt from 'antd'
class myInput extends React.Component {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
componentDidMount() {
this.inputRef.current.focus();
}
render() {
return (
<InputAnt ref={this.inputRef}/>
)
}
}
export default myInput;
Here the InputAnt does receive focus, but my concern is that : the 'myInput' component can be used by many other components to render an input along with so many other components. So maybe I don't want the input to get focus in all the cases wherever 'myInput' component is used, I only want to focus under particular scenarios, how can that be achieved?
Just pass an additional prop when you are using myinput.
<myInput shouldFocus />
and in the componentDidMount lifecycle hook:
componentDidMount() {
if (this.props.shouldFocus) {
this.inputRef.current.focus();
}
}
and when you want to use myInput but don't want to focus your input just use it as:
<myInput />
Related
I got an app that is working on react using a class component, i found a code of a feature that i would like to add to my code but it's made using a functional component. The code is here https://codesandbox.io/s/framer-motion-animate-in-view-gqcc8 but the relevant part is this.
import { useInView } from "react-intersection-observer";
import { motion, useAnimation } from "framer-motion";
import "./styles.css";
function Box() {
const controls = useAnimation();
const [ref, inView] = useInView();
useEffect(() => {
if (inView) {
controls.start("visible");
}
}, [controls, inView]);
I don't know how to add that controls variable in my class component
class App extends Component {
constructor(props) {
super(props);
this.state = {
curtains: null,
loading: true,
renderNav: false
};
}
Should i add it on my state? i don't understand how to make it works in class component
You can't use hooks inside of a class component. What you can do is to write a little wrapper that exposes the ref and controls in a render prop:
const Controls = ({children}) => {
const controls = useAnimation();
const [ref, inView] = useInView();
useEffect(() => {
if (inView) {
controls.start("visible");
}
}, [controls, inView]);
return children(ref, controls);
};
Then you can use it like this:
class App extends Component {
// ...
render() {
return (
<Controls>
{(ref, controls) => (
<motion.div ref={ref} animate={controls}>
{/* content */}
</motion.div>
)}
</Controls>
);
}
}
Lets say you have
const functionalComponent=()=>{
return <h1>Functional componenet</h1>
}
and you want to change it to class component
use this import at the top:
import React,{Component} from "react";
and change your code to something like this:
Class functionalComponent extends Component{
state={}
render(){
return <h1>functional component</h1>;
}
}
your functional component is now changed to class component.
And to use it in your existing class component , you don't need to change your functional component to class component unless you require local state.
with the introduction of react hooks that's also changed i.e, you don't have to change your functional component to class component if you plan to use hooks.
In your code : useEffect is a hook and you can't use it inside a class component.
I would recommend simply importing the functional component inside your class component and if you have to pass some value , you can pass it as a prop.
And as far as importing your functional component is concerned:
import React,{Component} from "react";
import Box from "./Box.js";
class App extends Component {
constructor(props) {
super(props);
this.state = {
curtains: null,
loading: true,
renderNav: false
};
render(){
return(<Box/>);
}
}
You can also use functional components anywhere like a class component. Btw is also using so no need to worry about the thing that you cannot use state in it.
Use:
<Box props={props}/>
As shown in the code below, the App component has a state. The properties of this state are passed down to the Child components via props.
But as soon as the state changes in the App component, the Child components inside the Sortable component are not updated. But Child components outside the this Sortable container is updated (rerendered) as usual.
How can I achieve these sortable Child components to be rendered on each state change?
Here's the code (simplified):
App.js:
import Sortable from 'react-sortablejs';
import Child from './Child';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
text1: "Text1",
text2: "Text2"
}
}
render() {
return (
<div className="App">
<Sortable>
<Child text={this.state.text1} />
<Child text={this.state.text2} />
</Sortable>
<Child text={this.state.text1} />
</div>
);
}
}
Child.js:
import React from 'react';
export default class Child extends React.Component {
constructor(props) {
super(props);
}
render() {
return (<p>{this.props.text}</p>)
}
}
How the page looks like when loaded:
... and after state is updated:
It looks like the Sortable component overrides shouldComponentUpdate here:
shouldComponentUpdate(nextProps) {
// If onChange is null, it is an UnControlled component
// Don't let React re-render it by setting return to false
if (!nextProps.onChange) {
return false;
}
return true;
}
Basically, it's set to only change if there is an onChange handler. You'd want to pass a method like that to determine what to do (if anything) when the state of App changes.
I'm new with Reactjs and I'm trying to use this.refs.myComponent to get the value of an imput field, but this input field is nested in another react component. Let me share an example of what I mean:
Imagine that i have this:
class ParentComponent extends React.Component {
onFormSubmit(e) {
console.log(this.refs.childName);
}
render() {
return (
<form onSubmit={this.onFormSubmit.bind(this)}>
<ChildComponent refName='childName'/>
<button type='submit'>Submit</button>
</form>
);
}
}
class ChildComponent extends React.Component {
render() {
return (
<input type='text' ref={this.props.refValue} name={this.props.refValue} id={this.props.refValue}/>
);
}
}
The problem is when I call this.refs.childName I can't take the value as part of the form submit event without doing something like evt.target.childName.value?
Regards
Generally speaking, refs are not the preferred way to handle passing UI data (state in React) down to child components. It's better to avoid using refs when possible.
This is a great explanation of the relationship between props, and components.
And this explains state and some of the core beliefs about state within the React framework.
So here is an example to accomplish what you are trying to do, in a "React friendly" way. Your ChildComponent can be stateless, so it only has one responsibility, to render the props passed down to it, whose props handled as state in ParentComponent.
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
const ChildComponent = (props) => {
return (
<input
type='text'
value={props.textValue}
onChange={props.onTextChange}
/>
)
}
class ParentComponent extends Component {
constructor(props) {
super(props)
this.onFormSubmit = this.onFormSubmit.bind(this)
this.onTextChange = this.onTextChange.bind(this)
this.state = {
textValue: ''
}
}
onFormSubmit(e) {
e.preventDefault()
console.log(`You typed: ${this.state.textValue}`)
}
onTextChange(e) {
this.setState({textValue: e.target.value})
}
render() {
return (
<form onSubmit={this.onFormSubmit}>
<ChildComponent
textValue={this.state.textValue}
onTextChange={this.onTextChange}
/>
<button type='submit'>Submit</button>
</form>
)
}
}
// assumes you have an element with an id of 'root'
// in the root html file every React app has
ReactDOM.render(<ParentComponent/>, document.getElementById('root'))
I would strongly recommend using create-react-app to get a quick and easy React app running without doing anything.
Hope it helps.
I have a few components that each contain inputs, and on the main component I have a button that will send that information all at once to the server. The problem is that the main component that has the button doesn't have the input content of the child components.
In the past I've passed down a method that would send the content back up into the state, but is there an easier less painful way of doing it? It just feels like an odd way of doing that.
Here's a short example of what I have and what I mean.
Main component:
import React from 'react';
import { Button } from 'react-toolbox/lib/button';
import Message from './Message';
class Main extends React.Component {
constructor() {
super();
this.state = { test: '' };
}
render() {
return (
<div className="container mainFrame">
<h2>Program</h2>
<Message />
</div>
);
}
}
export default Main;
And the message component:
import React from 'react';
import axios from 'axios';
import Input from 'react-toolbox/lib/input';
class Message extends React.Component {
constructor() {
super();
this.state = { message: '' };
this.handleChange = this.handleChange.bind(this);
}
handleChange(value) {
this.setState({ message: value });
}
render() {
return (
<Input
type="text"
label="Message"
name="name"
onChange={this.handleChange}
/>
);
}
}
export default Message;
To answer your question, yes. You can try using refs. Add a ref to Message component, and you will be able to access the child component's methods, state and everything. But thats not the conventional way, people generally use callbacks, as you mentioned earlier.
import React from 'react';
import { Button } from 'react-toolbox/lib/button';
import Message from './Message';
class Main extends React.Component {
constructor() {
super();
this.state = { test: '' };
}
clickHandler () {
let childState = this.refs.comp1.state //returns the child's state. not prefered.
let childValue = this.refs.comp1.getValue(); // calling a method that returns the child's value
}
render() {
return (
<div className="container mainFrame">
<h2>Program</h2>
<Message ref="comp1"/>
<Button onClick={this.clickHandler} />
</div>
);
}
}
export default Main;
import React from 'react';
import axios from 'axios';
import Input from 'react-toolbox/lib/input';
class Message extends React.Component {
constructor() {
super();
this.state = { message: '' };
this.handleChange = this.handleChange.bind(this);
}
handleChange(value) {
this.setState({ message: value });
}
getValue () {
return this.state.message;
}
render() {
return (
<Input
type="text"
label="Message"
name="name"
onChange={this.handleChange}
/>
);
}
}
export default Message;
You are doing what is suggested in docs so it's a good way.
I have a button that will send that information all at once to the server
I assume then it might be form you can use. If so you can just handle onSubmit event and create FormData object containing all nested input field names with their values (even in children components). No need for callbacks then.
handleSubmit(e){
e.preventDefault();
const form = e.currentTarget;
const formData = new FormData(form); // send it as a body of your request
// form data object will contain key value pairs corresponding to input `name`s and their values.
}
checkout Retrieving a FormData object from an HTML form
Currently to make controlled inputs work inside Stateless React components I am wrapping the stateless component inside a Sate full component.
For example,
const InputComponent = (props) => {
return (
<input value={props.name} onChange={props.handleChange} />
);
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'Tekeste'
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
name: event.target.value
});
}
render() {
return (
<InputComponent name={this.state.name} handleChange={this.handleChange} />
);
}
}
What I would like to know is a couple of things.
Is this a good pattern?
If not how can I achieve my goal i.e to have controlled inputs inside stateless components.
Since the InputComponent receives its value and the callback to modify it from its parent, it's a controlled input without a state. It's a perfectly good pattern, you can also make it even simpler using ES7 class properties like this:
class App extends React.Component {
state = {
name: 'Tekeste'
};
handleChange = (event) => {
this.setState({
name: event.target.value
});
}
render() {
return (
<InputComponent name={this.state.name} handleChange={this.handleChange} />
);
}
}
If you're using create-react-app, it's already supported out-of-the-box.
Also, you can rename the props of controlled input to value and onChange as they are more conventionally used.
building off of #frontsideair's answer, you could do something like the following using https://github.com/NullVoxPopuli/react-state-helpers
import React, { Component } from 'react';
import stateWrapper from 'react-state-helpers';
class App extends Component {
render() {
const {
mut,
values: { name }
} = this.props;
return <InputComponent name={name} handleChange={mut('name')} />;
}
}
export default stateWrapper(App)