Stateless React components with controlled input - javascript

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)

Related

ReactJS - TypeError: Cannot set property 'propOne' of undefined

I have a simple React component:
import React from "react";
export default class Car extends React.Component {
render() {
return <h2>I am a {this.props.name}</h2>;
}
}
I am then importing this component into another component. This new component has a textbox which onChange event, tries to set a property which is being passed as a property to component one.
import React from "react";
import Car from "./Car";
class Garage extends React.Component {
constructor(props) {
super(props);
this.propOne = "Ford";
this.state = { value: "initial Value" };
}
handleChange(event) {
this.propOne = event.target.value;
}
render() {
return (
<>
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
<Car name={this.propOne} />
</>
);
}
}
export default Garage;
After running this code, I am getting TypeError: Cannot set property 'propOne' of undefined error
It's because you are making a new function and creating a new instance of this. Change it to an arrow function -
handleChange = (event) => {
this.propOne = event.target.value;
}
Or you can bind the this of handleChange to parent's this in the constructor of class
constructor(props) {
super(props);
this.propOne = "Ford";
this.state = { value: "initial Value" };
this.handleChange = this.handleChange.bind(this);
}
Kudos to #Atin for mentioning changing your handleChange function to an arrow function or binding this (I'll use the arrow function in my answer).
You'll also need to set propOne in state, and use this.setState(...) to update the value of propOne so that your component re-renders when propOne changes. When you just change this.propOne, React doesn't know that your component needs to re-render with the updated value, which is why you don't see the effect in Car. -> that's what the state is for. Whenever state is changed (with this.setState), React knows it needs to re-render that component, and it will pass your updated state property down to child components.
Try this for your state:
this.state = { value: "initial Value", propOne: "Ford" };
And this for your handleChange function:
handleChange = (event) => {
this.setState({ propOne: event.target.value });
}
Edit:
Additionally, your code (and the above, fixed code) is setting a property propOne on the state, but you're using this.state.value to update your input text value. Is there any reason you're trying to use two different properties - value and propOne? It seems they're being used for the same purpose, and therefore you can replace propOne with value everywhere like this:
constructor(props) {
super(props);
this.state = { value: "initial Value" };
}
handleChange = (event) => {
this.setState({ value: event.target.value });
}
render() {
return (
<>
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
<Car name={this.state.value} />
</>
);
}
Referenced error is thrown because you didn't bind this.
Other than that, you have a bunch of errors in your code, so I tried to address them all at once. If you have further questions, feel free to ask in the comments:
const { render } = ReactDOM
class Car extends React.Component {
render() {
return <h2>I am a {this.props.name}</h2>
}
}
class Garage extends React.Component {
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this)
this.state = { value: "initial Value", propOne: 'Ford' }
}
handleChange(val) {
this.setState({value: val, propOne: val})
}
render() {
return (
<div>
<input
type="text"
value={this.state.value}
onChange={e => this.handleChange(e.target.value)}
/>
<Car name={this.state.propOne} />
</div>
)
}
}
render(
<Garage />,
document.getElementById('root')
)
<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><div id="root"></div>
p.s. and I agree with above answers, function components and hooks could make your app look much nicer

Call Method that inside Another Component - React Js

i need to know how to fetch state of component from other component by calling the seconed component method inside of first component ?
like :
class General extends Component {
state = {
input:"
}
fetchState() {
return this.state;
}
handleChange () {
this.setState({[e.target.name]: e.traget.value});
}
render() {
return <input type="text" name="input" onChange={this.handleChange.bind(this}>
}
}
class Car extends Component {
render() {
console.log( General.fetchState() );
return null;
}
}
i know i can use static method but i don't have access to this keyword.
The recommended way of doing that kind of things is by composing components and passing the parent's states as props
class General extends Component {
state = { ... }
render () {
return (
<Car {...this.state} />
)
}
}
class Car extends Component {
render () {
console.log(this.props)
return (...)
}
}
Now if you want to share a global state between components could be a good idea to use context api with hooks.
import React, { createContext, useContext } from "react";
import ReactDom from "react-dom";
const initialState = { sharedValue: "Simple is better" };
const StateContext = createContext({});
const General = () => {
const globalState = useContext(StateContext);
return <h1>General: {globalState.sharedValue}</h1>;
};
const Car = () => {
const globalState = useContext(StateContext);
return <h1>Car: {globalState.sharedValue}</h1>;
};
const App = () => {
return (
<StateContext.Provider value={initialState}>
<General />
<Car />
</StateContext.Provider>
);
};
ReactDom.render(
<App />,
document.getElementById("root")
);
Here is the example link.
And here I have a repo with a more elaborated example managing global state with just hooks.
There are many approaches, I suggest using a general state accessible from both components.
Check ReactN for simplicity or Redux for a more robust solution. Note Redux has a big learning curve and quite some boilerplate that, depending on the size of your App, it could not be necessary.
Using globals is not advisable on many situations, but to answer your question, you could also do this:
General component:
class General extends Component {
constructor(){
global.fetchGeneralState = this.fetchState;
}
fetchState = () => {
return this.state;
}
}
Then from the Car component, you can just call: global.fetchGeneralState(); and you will get the state from the General component.
In your current code, the only way to do it is to use new General.
console.log(new General().fetchState());
If you expect to use Car component as a parent of General component, then you can simply use ref. Here is the modified code of yours that I have tested :
import React from "react";
class General extends React.Component {
constructor () {
super();
this.state = {input: ""}
this.handleChange = this.handleChange.bind(this);
}
fetchState() {
return this.state;
}
handleChange(e) {
this.setState({[e.target.name]: e.target.value});
}
render() {
return <input type="text" name="input" onChange={this.handleChange} />
}
}
export default class Car extends React.Component {
constructor () {
super();
this.refToGeneral = React.createRef()
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
console.log(this.refToGeneral.current.fetchState())
}
render() {
return (
<>
<General ref={this.refToGeneral} />
<button type="button" onClick={this.handleClick}>Show State</button>
</>
)
}
}

Pass value from nested react component childs to the form onSubmit handle function

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.

How to change props of a child?

I am new to React. This is probably a noob question.
I want to change the "filteredFields" prop of my MeteorGriddle component when the user clicks a checkbox. My JSX:
const bookingsPage = () => {
let filteredFields = ['userName'];
const handleClick = (e) => {
if (e.target.checked) {
filteredFields = ['userEmail'];
// How to propagate this change to the MeteorGriddle?
}
}
return (
<div>
<label><input type="checkbox" defaultChecked="true" value="userEmail" onClick={handleClick}/>Email</label>
<MeteorGriddle filteredFields={filteredFields}/>
</div>
);
};
I see two ways of solving your problem.
The first and easy way to do this:
Turn your bookingsPage component into statefull component instead of functional,
then you'd be able to create state inside it, and then change the state on event alongside with passing it to MeteorGriddle component.
So the code would be:
class bookingsPage extends React.Component {
getInitialState = () => {
filteredFields: []
}
handleClick = (e) => {
if (e.target.checked) {
const newFilteredFields =
[ ...this.state.filteredFields ].push('userEmail');
this.setState({ filteredFields: newFilteredFields });
}
}
render() {
return (
<div>
<label>
<input
type="checkbox"
defaultChecked="true"
value="userEmail"
onClick={this.handleClick}
/>
Email
</label>
<MeteorGriddle
filteredFields={this.state.filteredFields}
/>
</div>
);
}
};
Second and harder way to do this:
Take a look on Redux. It solves a problem of data flow in React.
The basic concept is that when you check you checkbox, you dispatch an action into reducer (aka your global data storage for react components), and then GriddleComponent recieves new state of your application with fresh data inside which tells him the checkbox is checked.
Say if you want me to write an example based on yours for you.
As #Maxx says, you should use a component with state. Then when you call the setState method, it will render again, updating the props of the children.
In your case this should work (also using ES6 notation):
import React from 'react';
import MeteorGriddle from '...whatever path...';
class bookingsPage extends React.Component {
state = {
filteredFields: ['userName']
};
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
if (e.target.checked) {
this.setState({
...this.state, //This is just a good practice to not overwrite other properties
filteredFields: ['userEmail']
});
}
}
render() {
const { filteredFields } = this.state;
return(
<div>
<label>
<input type="checkbox" defaultChecked="true" value="userEmail"
onChange={this.handleChange}/>Email
</label>
<MeteorGriddle filteredFields={filteredFields}/>
</div>
);
}
}
There are number of ways to achieve this, you can just try like the below code,
import React from 'react';
class bookingsPage extends React.Component {
state = {
filteredFields: ['userName']
};
constructor(props) {
super(props);
}
handleChange(e) {
if (e.target.checked) {
this.setState({
filteredFields: ['userEmail']
});
}
}
render() {
return(
<div>
<label>
<input type="checkbox" defaultChecked="true" value="userEmail"
onChange={this.handleChange.bind(this)}/>Email
</label>
<MeteorGriddle filteredFields={this.state.filteredFields}/>
</div>
);
}}

How can I update the parent's state in React?

My structure looks as follows:
Component 1
- |- Component 2
- - |- Component 4
- - - |- Component 5
Component 3
Component 3 should display some data depending on state of Component 5.
Since props are immutable, I can't simply save its state in Component 1 and forward it, right? And yes, I've read about Redux, but I don't want to use it. I hope that it's possible to solve it just with react. Am I wrong?
For child-parent communication you should pass a function setting the state from parent to child, like this
class Parent extends React.Component {
constructor(props) {
super(props)
this.handler = this.handler.bind(this)
}
handler() {
this.setState({
someVar: 'some value'
})
}
render() {
return <Child handler = {this.handler} />
}
}
class Child extends React.Component {
render() {
return <Button onClick = {this.props.handler}/ >
}
}
This way the child can update the parent's state with the call of a function passed with props.
But you will have to rethink your components' structure, because as I understand components 5 and 3 are not related.
One possible solution is to wrap them in a higher level component which will contain the state of both component 1 and 3. This component will set the lower level state through props.
This is how to do it with the new useState hook.
Method - Pass the state changer function as a props to the child component and do whatever you want to do with the function:
import React, {useState} from 'react';
const ParentComponent = () => {
const[state, setState]=useState('');
return(
<ChildComponent stateChanger={setState} />
)
}
const ChildComponent = ({stateChanger, ...rest}) => {
return(
<button onClick={() => stateChanger('New data')}></button>
)
}
I found the following working solution to pass the onClick function argument from the child to the parent component:
Version with passing a method()
//ChildB component
class ChildB extends React.Component {
render() {
var handleToUpdate = this.props.handleToUpdate;
return (<div><button onClick={() => handleToUpdate('someVar')}>
Push me
</button>
</div>)
}
}
//ParentA component
class ParentA extends React.Component {
constructor(props) {
super(props);
var handleToUpdate = this.handleToUpdate.bind(this);
var arg1 = '';
}
handleToUpdate(someArg){
alert('We pass argument from Child to Parent: ' + someArg);
this.setState({arg1:someArg});
}
render() {
var handleToUpdate = this.handleToUpdate;
return (<div>
<ChildB handleToUpdate = {handleToUpdate.bind(this)} /></div>)
}
}
if(document.querySelector("#demo")){
ReactDOM.render(
<ParentA />,
document.querySelector("#demo")
);
}
Look at JSFiddle
Version with passing an Arrow function
//ChildB component
class ChildB extends React.Component {
render() {
var handleToUpdate = this.props.handleToUpdate;
return (<div>
<button onClick={() => handleToUpdate('someVar')}>
Push me
</button>
</div>)
}
}
//ParentA component
class ParentA extends React.Component {
constructor(props) {
super(props);
}
handleToUpdate = (someArg) => {
alert('We pass argument from Child to Parent: ' + someArg);
}
render() {
return (<div>
<ChildB handleToUpdate = {this.handleToUpdate} /></div>)
}
}
if(document.querySelector("#demo")){
ReactDOM.render(
<ParentA />,
document.querySelector("#demo")
);
}
Look at JSFiddle
I want to thank the most upvoted answer for giving me the idea of my own problem basically the variation of it with arrow function and passing param from child component:
class Parent extends React.Component {
constructor(props) {
super(props)
// without bind, replaced by arrow func below
}
handler = (val) => {
this.setState({
someVar: val
})
}
render() {
return <Child handler = {this.handler} />
}
}
class Child extends React.Component {
render() {
return <Button onClick = {() => this.props.handler('the passing value')}/ >
}
}
Hope it helps someone.
I like the answer regarding passing functions around. It's a very handy technique.
On the flip side you can also achieve this using pub/sub or using a variant, a dispatcher, as Flux does. The theory is super simple. Have component 5 dispatch a message which component 3 is listening for. Component 3 then updates its state which triggers the re-render. This requires stateful components, which, depending on your viewpoint, may or may not be an anti-pattern. I'm against them personally and would rather that something else is listening for dispatches and changes state from the very top-down (Redux does this, but it adds additional terminology).
import { Dispatcher } from 'flux'
import { Component } from 'React'
const dispatcher = new Dispatcher()
// Component 3
// Some methods, such as constructor, omitted for brevity
class StatefulParent extends Component {
state = {
text: 'foo'
}
componentDidMount() {
dispatcher.register( dispatch => {
if ( dispatch.type === 'change' ) {
this.setState({ text: 'bar' })
}
}
}
render() {
return <h1>{ this.state.text }</h1>
}
}
// Click handler
const onClick = event => {
dispatcher.dispatch({
type: 'change'
})
}
// Component 5 in your example
const StatelessChild = props => {
return <button onClick={ onClick }>Click me</button>
}
The dispatcher bundles with Flux is very simple. It simply registers callbacks and invokes them when any dispatch occurs, passing through the contents on the dispatch (in the above terse example there is no payload with the dispatch, simply a message id). You could adapt this to traditional pub/sub (e.g., using the EventEmitter from events, or some other version) very easily if that makes more sense to you.
I found the following working solution to pass the onClick function argument from the child to the parent component with a parameter:
Parent class:
class Parent extends React.Component {
constructor(props) {
super(props)
// Bind the this context to the handler function
this.handler = this.handler.bind(this);
// Set some state
this.state = {
messageShown: false
};
}
// This method will be sent to the child component
handler(param1) {
console.log(param1);
this.setState({
messageShown: true
});
}
// Render the child component and set the action property with the handler as value
render() {
return <Child action={this.handler} />
}}
Child class:
class Child extends React.Component {
render() {
return (
<div>
{/* The button will execute the handler function set by the parent component */}
<Button onClick={this.props.action.bind(this,param1)} />
</div>
)
} }
Whenever you require to communicate between a child to the parent at any level down, then it's better to make use of context. In the parent component define the context that can be invoked by the child, such as:
In the parent component, in your case component 3,
static childContextTypes = {
parentMethod: React.PropTypes.func.isRequired
};
getChildContext() {
return {
parentMethod: (parameter_from_child) => this.parentMethod(parameter_from_child)
};
}
parentMethod(parameter_from_child){
// Update the state with parameter_from_child
}
Now in the child component (component 5 in your case), just tell this component that it wants to use the context of its parent.
static contextTypes = {
parentMethod: React.PropTypes.func.isRequired
};
render() {
return(
<TouchableHighlight
onPress = {() => this.context.parentMethod(new_state_value)}
underlayColor='gray' >
<Text> update state in parent component </Text>
</TouchableHighlight>
)}
You can find the Demo project in this GitHub repository.
It seems that we can only pass data from parent to child as React promotes unidirectional data flow, but to make the parent update itself when something happens in its "child component", we generally use what is called a "callback function".
We pass the function defined in the parent to the child as "props" and
call that function from the child triggering it in the parent
component.
class Parent extends React.Component {
handler = (Value_Passed_From_SubChild) => {
console.log("Parent got triggered when a grandchild button was clicked");
console.log("Parent->Child->SubChild");
console.log(Value_Passed_From_SubChild);
}
render() {
return <Child handler = {this.handler} />
}
}
class Child extends React.Component {
render() {
return <SubChild handler = {this.props.handler}/ >
}
}
class SubChild extends React.Component {
constructor(props){
super(props);
this.state = {
somethingImp : [1,2,3,4]
}
}
render() {
return <button onClick = {this.props.handler(this.state.somethingImp)}>Clickme<button/>
}
}
React.render(<Parent />,document.getElementById('app'));
HTML
----
<div id="app"></div>
In this example we can make data pass from sub child → child → parent by passing function to its direct child.
Most of the answers given previously are for React.Component-based designs. If you are using useState in the recent upgrades of the React library, then follow this answer.
I've used a top rated answer from this page many times, but while learning React, I've found a better way to do that, without binding and without an inline function inside props.
Just look here:
class Parent extends React.Component {
constructor() {
super();
this.state = {
someVar: value
}
}
handleChange = (someValue) => {
this.setState({someVar: someValue})
}
render() {
return <Child handler={this.handleChange} />
}
}
export const Child = ({handler}) => {
return <Button onClick={handler} />
}
The key is in an arrow function:
handleChange = (someValue) => {
this.setState({someVar: someValue})
}
You can read more here.
Simply pass the parent's setState function via props to the child component.
function ParentComp() {
const [searchValue, setSearchValue] = useState("");
return <SearchBox setSearchValue={setSearchValue} searchValue={searchValue} />;
}
then in child component:
function SearchBox({ searchValue, setSearchValue }) {
return (
<input
id="search-post"
type="text"
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
placeholder="Search Blogs ..."
/>
)
}
A second example to handle click from child component:
// We've below function and component in parent component
const clickHandler = (val) => {
alert(`httpRequest sent. \nValue Received: ${val}`);
};
// JSX
<HttpRequest clickHandler={clickHandler} />
this is how you get function from parent component then pass a value and fire clickHandler through it.
function HttpRequest({ clickHandler }) {
const [content, setContent] = useState("initialState");
return (
<button onClick={() => clickHandler(content)}>
Send Request
</button>
);
}
export default HttpRequest;
We can create ParentComponent and with a handleInputChange method to update the ParentComponent state. Import the ChildComponent and we pass two props from the parent to the child component i.e., the handleInputChange function and count.
import React, { Component } from 'react';
import ChildComponent from './ChildComponent';
class ParentComponent extends Component {
constructor(props) {
super(props);
this.handleInputChange = this.handleInputChange.bind(this);
this.state = {
count: '',
};
}
handleInputChange(e) {
const { value, name } = e.target;
this.setState({ [name]: value });
}
render() {
const { count } = this.state;
return (
<ChildComponent count={count} handleInputChange={this.handleInputChange} />
);
}
}
Now we create the ChildComponent file and save it as ChildComponent.jsx. This component is stateless because the child component doesn't have a state. We use the prop-types library for props type checking.
import React from 'react';
import { func, number } from 'prop-types';
const ChildComponent = ({ handleInputChange, count }) => (
<input onChange={handleInputChange} value={count} name="count" />
);
ChildComponent.propTypes = {
count: number,
handleInputChange: func.isRequired,
};
ChildComponent.defaultProps = {
count: 0,
};
export default ChildComponent;
If you want to update the parent component,
class ParentComponent extends React.Component {
constructor(props){
super(props);
this.state = {
page: 0
}
}
handler(val){
console.log(val) // 1
}
render(){
return (
<ChildComponent onChange={this.handler} />
)
}
}
class ChildComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
page: 1
};
}
someMethod = (page) => {
this.setState({ page: page });
this.props.onChange(page)
}
render() {
return (
<Button
onClick={() => this.someMethod()}
> Click
</Button>
)
}
}
Here onChange is an attribute with "handler" method bound to its instance. We passed the method handler to the Child class component, to receive via the onChange property in its props argument.
The attribute onChange will be set in a props object like this:
props = {
onChange: this.handler
}
and passed to the child component.
So the child component can access the value of name in the props object like this props.onChange.
It's done through the use of render props.
Now the child component has a button “Click” with an onclick event set to call the handler method passed to it via onChange in its props argument object. So now this.props.onChange in the child holds the output method in the parent class.
Reference and credits: Bits and Pieces
If this same scenario is not spread everywhere you can use React's context, especially if you don't want to introduce all the overhead that state management libraries introduce. Plus, it's easier to learn. But be careful; you could overuse it and start writing bad code. Basically you define a Container component (that will hold and keep that piece of state for you) making all the components interested in writing/reading that piece of data to/from its children (not necessarily direct children).
Context - React
You could also use a plain React properly instead.
<Component5 onSomethingHappenedIn5={this.props.doSomethingAbout5} />
Pass doSomethingAbout5 up to Component 1:
<Component1>
<Component2 onSomethingHappenedIn5={somethingAbout5 => this.setState({somethingAbout5})}/>
<Component5 propThatDependsOn5={this.state.somethingAbout5}/>
<Component1/>
If this is a common problem, you should starting thinking moving the whole state of the application to somewhere else. You have a few options, the most common are:
Redux
Flux
Basically, instead of managing the application state in your component you send commands when something happens to get the state updated. Components pull the state from this container as well so all the data is centralized. This doesn't mean you can't use local state any more, but that's a more advanced topic.
We can set the parent state from a child component by passing a function into the child component as props as below:
class Parent extends React.Component{
state = { term : ''}
onInputChange = (event) => {
this.setState({term: event.target.value});
}
onFormSubmit = (event) => {
event.preventDefault();
this.props.onFormSubmit(this.state.term);
}
render(){
return (
<Child onInputChange={this.onInputChange} onFormSubmit=
{this.onFormSubmit} />
)
}
}
class Child extends React.Component{
render(){
return (
<div className="search-bar ui segment">
<form className="ui form" onSubmit={this.props.onFormSubmit}>
<div class="field">
<label>Search Video</label>
<input type="text" value={this.state.term} onChange=
{this.props.onInputChange} />
</div>
</form>
</div>
)
}
}
This way, the child will update the parent state onInputChange and onFormSubmit are props passed from parents. This can be called from event listeners in the child, hence the state will get updated there.
Parent Component
function Parent() {
const [value, setValue] = React.useState("");
function handleChange(newValue) {
setValue(newValue);
}
// We pass a callback to Child
return <Child value={value} onChange={handleChange} />;
}
Child Component
function Child(props) {
function handleChange(event) {
// Here, we invoke the callback with the new value
props.onChange(event.target.value);
}
return <input value={props.value} onChange={handleChange} />
}
Here is a short snippet to get two ways binding data.
The counter show the value from the parent and is updated from the child
class Parent extends React.Component {
constructor(props) {
super(props)
this.handler = this.handler.bind(this)
this.state = {
count: 0
}
}
handler() {
this.setState({
count: this.state.count + 1
})
}
render() {
return <Child handler={this.handler} count={this.state.count} />
}
}
class Child extends React.Component {
render() {
return <button onClick={this.props.handler}>Count {this.props.count}</button>
}
}
ReactDOM.render(<Parent />, document.getElementById("root"));
<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>
<div id="root"></div>
This is the way I do it:
type ParentProps = {}
type ParentState = { someValue: number }
class Parent extends React.Component<ParentProps, ParentState> {
constructor(props: ParentProps) {
super(props)
this.state = { someValue: 0 }
this.handleChange = this.handleChange.bind(this)
}
handleChange(value: number) {
this.setState({...this.state, someValue: value})
}
render() {
return <div>
<Child changeFunction={this.handleChange} defaultValue={this.state.someValue} />
<p>Value: {this.state.someValue}</p>
</div>
}
}
type ChildProps = { defaultValue: number, changeFunction: (value: number) => void}
type ChildState = { anotherValue: number }
class Child extends React.Component<ChildProps, ChildState> {
constructor(props: ChildProps) {
super(props)
this.state = { anotherValue: this.props.defaultValue }
this.handleChange = this.handleChange.bind(this)
}
handleChange(value: number) {
this.setState({...this.state, anotherValue: value})
this.props.changeFunction(value)
}
render() {
return <div>
<input onChange={event => this.handleChange(Number(event.target.value))} type='number' value={this.state.anotherValue}/>
</div>
}
}
As per your question, I understand that you need to display some conditional data in Component 3 which is based on the state of Component 5. Approach:
The state of Component 3 will hold a variable to check whether Component 5's state has that data
An arrow function which will change Component 3's state variable.
Passing an arrow function to Component 5 with props.
Component 5 has an arrow function which will change Component 3's state variable
An arrow function of Component 5 called on loading itself
<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>
Class Component3 extends React.Component {
state = {
someData = true
}
checkForData = (result) => {
this.setState({someData : result})
}
render() {
if(this.state.someData) {
return(
<Component5 hasData = {this.checkForData} />
//Other Data
);
}
else {
return(
//Other Data
);
}
}
}
export default Component3;
class Component5 extends React.Component {
state = {
dataValue = "XYZ"
}
checkForData = () => {
if(this.state.dataValue === "XYZ") {
this.props.hasData(true);
}
else {
this.props.hasData(false);
}
}
render() {
return(
<div onLoad = {this.checkForData}>
//Conditional Data
</div>
);
}
}
export default Component5;
To set state of parent in the child you can use callback.
const Child = ({handleClick}) => (
<button on click={() => handleClick('some vale')}>change value</button>
)
const parent = () => {
const [value, setValue] = useState(null)
return <Child handleClick={setValue} />
}
In your structure it seems Components 1 an 3 are brothers. So you has 3 options:
1- Put the state into the parent of them(not recommended for 4 layer parent-child).
2- Use useContext and useRducer(or useState) together.
3- Use state managers like redux, mobx ...
This seem to work for me
Parent:
...
const [open, setOpen] = React.useState(false);
const handleDrawerClose = () => {
setOpen(false);
};
...
return (
<PrimaryNavigationAccordion
handleDrawerClose={handleDrawerClose}
/>
);
Child:
...
export default function PrimaryNavigationAccordion({
props,
handleDrawerClose,
})
...
<Link
to={menuItem.url}
component={RouterLink}
color="inherit"
underline="hover"
onClick={() => handleDrawerClose()}
>
{menuItem.label}
</Link>
You can do it by passing a reference for the parent to child, as:
Having a parent component A in A.js with a method updateAState
Having a child component B in B.js
Having a wrapper function that renders <A><B></B></A> in C.js
In C.js you can use useRef as following:
import React, { useRef } from "react";
export default function C()
{
const parentARef = useRef();
const handleChildBClick = () => parentARef.current.updateAState();
return (
<A ref={parentARef}>
<B onClick={handleChildBClick}>
</B>
</A>
);
}
Guidance Reference: https://stackoverflow.com/a/56496607/1770571
Data cannot be passed from child to parent in React. Data must be passed from parent to child. In this case, you can use either the built-in Context API or a third-party state management solution such as Redux, Mobx, or Apollo GraphQL. However, if your app structure is too small, you can store your data in your parent element and then send it to your child via prop drilling. But if your project is larger, it will be messy.
<Footer
action={()=>this.setState({showChart: true})}
/>
<footer className="row">
<button type="button" onClick={this.props.action}>Edit</button>
{console.log(this.props)}
</footer>
Try this example to write inline setState, it avoids creating another function.

Categories