score:207
so, if i'm understanding you correctly, your first solution is suggesting that you're keeping state in your root component? i can't speak for the creators of react, but generally, i find this to be a proper solution.
maintaining state is one of the reasons (at least i think) that react was created. if you've ever implemented your own state pattern client side for dealing with a dynamic ui that has a lot of interdependent moving pieces, then you'll love react, because it alleviates a lot of this state management pain.
by keeping state further up in the hierarchy, and updating it through eventing, your data flow is still pretty much unidirectional, you're just responding to events in the root component, you're not really getting the data there via two way binding, you're telling the root component that "hey, something happened down here, check out the values" or you're passing the state of some data in the child component up in order to update the state. you changed the state in c1, and you want c2 to be aware of it, so, by updating the state in the root component and re-rendering, c2's props are now in sync since the state was updated in the root component and passed along.
class example extends react.component {
constructor (props) {
super(props)
this.state = { data: 'test' }
}
render () {
return (
<div>
<c1 onupdate={this.onupdate.bind(this)}/>
<c2 data={this.state.data}/>
</div>
)
}
onupdate (data) { this.setstate({ data }) }
}
class c1 extends react.component {
render () {
return (
<div>
<input type='text' ref='myinput'/>
<input type='button' onclick={this.update.bind(this)} value='update c2'/>
</div>
)
}
update () {
this.props.onupdate(this.refs.myinput.getdomnode().value)
}
})
class c2 extends react.component {
render () {
return <div>{this.props.data}</div>
}
})
reactdom.rendercomponent(<example/>, document.body)
score:0
- the right thing to do is to have the state in the parent component, to avoid ref and what not
- an issue is to avoid constantly updating all children when typing into a field
- therefore, each child should be a component (as in not a purecomponent) and implement
shouldcomponentupdate(nextprops, nextstate)
- this way, when typing into a form field, only that field updates
the code below uses @bound
annotations from es.next babel-plugin-transform-decorators-legacy
of babeljs 6 and class-properties (the annotation sets this value on member functions similar to bind):
/*
© 2017-present harald rudell <harald.rudell@gmail.com> (http://www.haraldrudell.com)
all rights reserved.
*/
import react, {component} from 'react'
import {bound} from 'class-bind'
const m = 'form'
export default class parent extends component {
state = {one: 'one', two: 'two'}
@bound submit(e) {
e.preventdefault()
const values = {...this.state}
console.log(`${m}.submit:`, values)
}
@bound fieldupdate({name, value}) {
this.setstate({[name]: value})
}
render() {
console.log(`${m}.render`)
const {state, fieldupdate, submit} = this
const p = {fieldupdate}
return (
<form onsubmit={submit}> {/* loop removed for clarity */}
<child name='one' value={state.one} {...p} />
<child name='two' value={state.two} {...p} />
<input type="submit" />
</form>
)
}
}
class child extends component {
value = this.props.value
@bound update(e) {
const {value} = e.target
const {name, fieldupdate} = this.props
fieldupdate({name, value})
}
shouldcomponentupdate(nextprops) {
const {value} = nextprops
const dorender = value !== this.value
if (dorender) this.value = value
return dorender
}
render() {
console.log(`child${this.props.name}.render`)
const {value} = this.props
const p = {value}
return <input {...p} onchange={this.update} />
}
}
score:0
the concept of passing data from parent to child and vice versa is explained.
import react, { component } from "react";
import reactdom from "react-dom";
// taken refrence from https://gist.github.com/sebkouba/a5ac75153ef8d8827b98
//example to show how to send value between parent and child
// props is the data which is passed to the child component from the parent component
class parent extends component {
constructor(props) {
super(props);
this.state = {
fieldval: ""
};
}
onupdateparent = val => {
this.setstate({
fieldval: val
});
};
render() {
return (
// to achieve the child-parent communication, we can send a function
// as a prop to the child component. this function should do whatever
// it needs to in the component e.g change the state of some property.
//we are passing the function onupdateparent to the child
<div>
<h2>parent</h2>
value in parent component state: {this.state.fieldval}
<br />
<child onupdate={this.onupdateparent} />
<br />
<otherchild passedval={this.state.fieldval} />
</div>
);
}
}
class child extends component {
constructor(props) {
super(props);
this.state = {
fieldvalchild: ""
};
}
updatevalues = e => {
console.log(e.target.value);
this.props.onupdate(e.target.value);
// onupdateparent would be passed here and would result
// into onupdateparent(e.target.value) as it will replace this.props.onupdate
//with itself.
this.setstate({ fieldvalchild: e.target.value });
};
render() {
return (
<div>
<h4>child</h4>
<input
type="text"
placeholder="type here"
onchange={this.updatevalues}
value={this.state.fieldval}
/>
</div>
);
}
}
class otherchild extends component {
render() {
return (
<div>
<h4>otherchild</h4>
value in otherchild props: {this.props.passedval}
<h5>
the child can directly get the passed value from parent by this.props{" "}
</h5>
</div>
);
}
}
reactdom.render(<parent />, document.getelementbyid("root"));
score:1
you should learn redux and reactredux library.it will structure your states and props in one store and you can access them later in your components .
score:1
with react >= 16.3 you can use ref and forwardref, to gain access to child's dom from its parent. don't use old way of refs anymore.
here is the example using your case :
import react, { component } from 'react';
export default class p extends react.component {
constructor (props) {
super(props)
this.state = {data: 'test' }
this.onupdate = this.onupdate.bind(this)
this.ref = react.createref();
}
onupdate(data) {
this.setstate({data : this.ref.current.value})
}
render () {
return (
<div>
<c1 ref={this.ref} onupdate={this.onupdate}/>
<c2 data={this.state.data}/>
</div>
)
}
}
const c1 = react.forwardref((props, ref) => (
<div>
<input type='text' ref={ref} onchange={props.onupdate} />
</div>
));
class c2 extends react.component {
render () {
return <div>c2 reacts : {this.props.data}</div>
}
}
see refs and forwardref for detailed info about refs and forwardref.
score:4
more recent answer with an example, which uses react.usestate
keeping the state in the parent component is the recommended way. the parent needs to have an access to it as it manages it across two children components. moving it to the global state, like the one managed by redux, is not recommended for same same reason why global variable is worse than local in general in software engineering.
when the state is in the parent component, the child can mutate it if the parent gives the child value
and onchange
handler in props (sometimes it is called value link or state link pattern). here is how you would do it with hooks:
function parent() {
var [state, setstate] = react.usestate('initial input value');
return <>
<child1 value={state} onchange={(v) => setstate(v)} />
<child2 value={state}>
</>
}
function child1(props) {
return <input
value={props.value}
onchange={e => props.onchange(e.target.value)}
/>
}
function child2(props) {
return <p>content of the state {props.value}</p>
}
the whole parent component will re-render on input change in the child, which might be not an issue if the parent component is small / fast to re-render. the re-render performance of the parent component still can be an issue in the general case (for example large forms). this is solved problem in your case (see below).
state link pattern and no parent re-render are easier to implement using the 3rd party library, like hookstate - supercharged react.usestate
to cover variety of use cases, including your's one. (disclaimer: i am an author of the project).
here is how it would look like with hookstate. child1
will change the input, child2
will react to it. parent
will hold the state but will not re-render on state change, only child1
and child2
will.
import { usestatelink } from '@hookstate/core';
function parent() {
var state = usestatelink('initial input value');
return <>
<child1 state={state} />
<child2 state={state}>
</>
}
function child1(props) {
// to avoid parent re-render use local state,
// could use `props.state` instead of `state` below instead
var state = usestatelink(props.state)
return <input
value={state.get()}
onchange={e => state.set(e.target.value)}
/>
}
function child2(props) {
// to avoid parent re-render use local state,
// could use `props.state` instead of `state` below instead
var state = usestatelink(props.state)
return <p>content of the state {state.get()}</p>
}
ps: there are many more examples here covering similar and more complicated scenarios, including deeply nested data, state validation, global state with setstate
hook, etc. there is also complete sample application online, which uses the hookstate and the technique explained above.
score:5
the first solution, with keeping the state in parent component, is the correct one. however, for more complex problems, you should think about some state management library, redux is the most popular one used with react.
score:5
i'm surprised that there are no answers with a straightforward idiomatic react solution at the moment i'm writing. so here's the one (compare the size and complexity to others):
class p extends react.component {
state = { foo : "" };
render(){
const { foo } = this.state;
return (
<div>
<c1 value={ foo } onchange={ x => this.setstate({ foo : x })} />
<c2 value={ foo } />
</div>
)
}
}
const c1 = ({ value, onchange }) => (
<input type="text"
value={ value }
onchange={ e => onchange( e.target.value ) } />
);
const c2 = ({ value }) => (
<div>reacting on value change: { value }</div>
);
i'm setting the state of a parent element from a child element. this seems to betray the design principle of react: single-direction data flow.
any controlled input
(idiomatic way of working with forms in react) updates the parent state in its onchange
callback and still doesn't betray anything.
look carefully at c1 component, for instance. do you see any significant difference in the way how c1
and built-in input
component handle the state changes? you should not, because there is none. lifting up the state and passing down value/onchange pairs is idiomatic for raw react. not usage of refs, as some answers suggest.
score:9
five years later with introduction of react hooks there is now much more elegant way of doing it with use usecontext hook.
you define context in a global scope, export variables, objects and functions in the parent component and then wrap children in the app in a context provided and import whatever you need in child components. below is a proof of concept.
import react, { usestate, usecontext } from "react";
import reactdom from "react-dom";
import styles from "./styles.css";
// create context container in a global scope so it can be visible by every component
const contextcontainer = react.createcontext(null);
const initialappstate = {
selected: "nothing"
};
function app() {
// the app has a state variable and update handler
const [appstate, updateappstate] = usestate(initialappstate);
return (
<div>
<h1>passing state between components</h1>
{/*
this is a context provider. we wrap in it any children that might want to access
app's variables.
in 'value' you can pass as many objects, functions as you want.
we wanna share appstate and its handler with child components,
*/}
<contextcontainer.provider value={{ appstate, updateappstate }}>
{/* here we load some child components */}
<book title="got" price="10" />
<debugnotice />
</contextcontainer.provider>
</div>
);
}
// child component book
function book(props) {
// inside the child component you can import whatever the context provider allows.
// earlier we passed value={{ appstate, updateappstate }}
// in this child we need the appstate and the update handler
const { appstate, updateappstate } = usecontext(contextcontainer);
function handlecommentchange(e) {
//here on button click we call updateappstate as we would normally do in the app
// it adds/updates comment property with input value to the appstate
updateappstate({ ...appstate, comment: e.target.value });
}
return (
<div classname="book">
<h2>{props.title}</h2>
<p>${props.price}</p>
<input
type="text"
//controlled component. value is reverse vound the value of the variable in state
value={appstate.comment}
onchange={handlecommentchange}
/>
<br />
<button
type="button"
// here on button click we call updateappstate as we would normally do in the app
onclick={() => updateappstate({ ...appstate, selected: props.title })}
>
select this book
</button>
</div>
);
}
// just another child component
function debugnotice() {
// inside the child component you can import whatever the context provider allows.
// earlier we passed value={{ appstate, updateappstate }}
// but in this child we only need the appstate to display its value
const { appstate } = usecontext(contextcontainer);
/* here we pretty print the current state of the appstate */
return (
<div classname="state">
<h2>appstate</h2>
<pre>{json.stringify(appstate, null, 2)}</pre>
</div>
);
}
const rootelement = document.body;
reactdom.render(<app />, rootelement);
you can run this example in the code sandbox editor.
score:36
having used react to build an app now, i'd like to share some thoughts to this question i asked half a year ago.
i recommend you to read
the first post is extremely helpful to understanding how you should structure your react app.
flux answers the question why should you structure your react app this way (as opposed to how to structure it). react is only 50% of the system, and with flux you get to see the whole picture and see how they constitute a coherent system.
back to the question.
as for my first solution, it is totally ok to let the handler go the reverse direction, as the data is still going single-direction.
however, whether letting a handler trigger a setstate in p can be right or wrong depending on your situation.
if the app is a simple markdown converter, c1 being the raw input and c2 being the html output, it's ok to let c1 trigger a setstate in p, but some might argue this is not the recommended way to do it.
however, if the app is a todo list, c1 being the input for creating a new todo, c2 the todo list in html, you probably want to handler to go two level up than p -- to the dispatcher
, which let the store
update the data store
, which then send the data to p and populate the views. see that flux article. here is an example: flux - todomvc
generally, i prefer the way described in the todo list example. the less state you have in your app the better.
Source: stackoverflow.com
Related Query
- React – the right way to pass form element state to sibling/parent elements?
- How to pass input values from a child Form component to the state of its parent component for submission with react hooks?
- How to pass state from child to parent by right way in React
- What is the right way to assign element of an array to state using React hooks?
- Is there a right way to pass data into a React component from the HTML page?
- Is window.__INITIAL_STATE__ still the preferred way to pass initial state to the client in React Universal apps?
- React what's the right way to get a parent props from its children
- What is the simplest way to pass state while using React Router?
- How to pass a state to a Parent component if I have a button triggering the state in the 2nd Child in React
- What is the right way to toggle a value inside object state in React hooks?
- What's the right way to change the state of another component in React
- Passing the value of a clicked element in a child component into a button in a parent component in react via state lifting not working
- What is the correct way to pass an argument to parent component in React from onClick event handler
- Refreshing react page does not pass parent state to the child state
- How to pass a method into a child element if the method relies on state variables in React
- Setting react state the right way
- Restyling parent elements the React way
- pass boolean state value from parent class to child class when a button is clicked in the parent class in react js so that i can load quizes with bool
- How to avoid mutating local state in React in the right and performant way
- How to pass state (select value) to the parent in React (hooks)
- Is this the right way to pass state to Redux action?
- Is there a way to give a Semantic UI React Dropdown the html "name" attribute so it will behave like a normal input element on a form submit?
- React Typescript. The right way to push new objects form api to array with existing objects without repeating them
- How to use react hooks to pass a state from child component to the parent component?
- React + Redux - What's the best way to handle CRUD in a form component?
- How to update the state of a sibling component from another sibling or imported component in REACT
- Pass React Ref from parent to child in order to get DOM element
- React Hooks : Why is it bad practice to pass the set state funcitons to a Child Component?
- Form created with React doesn't update the backing state object
- Which is the right way to detect first render in a react component
More Query from same tag
- How to use setTimeout with a Loader
- How to group nested array of objects in js
- React - Testing component propTypes - expecting a failure
- How to concatenate JSX elements into an array?
- Why do I get an object error when using useEffect and setInterval in my React app?
- React useReducer and context: how to provide state selectors?
- How select part of text in a Textfield on onFocus event with material-UI in React?
- Redux & React-router : Provider bug
- ReactJs + Redux ---> Try to call name of business where item.business_id === businessId
- React Apollo Error: Invariant Violation: Could not find "client" in the context or passed in as an option
- Reactjs change background of Quizcard onClick
- Error saying Parsing Error: Unexpected token
- Target fragment within react const
- get localstorage value and switch store in redux
- How to change focusedOption in React-select
- how to revert (last clicked list item) back to its original color when any other item is clicked - react hooks
- How to persist data from useLocation()?
- How can I disappear "button" element in react?
- How override material ui style with hooks
- Axios.delete(url[,config]): Type has no properties in common with type 'AxiosRequestConfig'
- How to show custom text (placeholder) in KeyboardDatePicker
- Build a React App in Elastic Beanstalk
- Limit content editable table data input to only one number with one decimal
- How do I add borderTop to React Material UI Table?
- onChange access funtion not working in react js
- onIonChange is triggering more than one input on Ionic 5 React
- React props.children width
- Find the time duration/length of an audio file uploaded to a React application
- TypeError: http.ServerResponse is undefined
- Serving React application with Apache works, but loading JSON from same server fails with 503