I have creator, I mean step1 -next-> step2 -next-> ...
In my parent component I have buttons preview and next, steps content are render as child.
class MyCreator extends React.Component {
render() {
return (
<div>
{this.renderStep(this.props.step.id)}
</div>
<div>
<button>back</button>
<button>next</button>
</div>
);
}
}
In a step I have a component which has only two methods: getData, setData. This is a third party component (so I cannot change implementation).
When I click button next I want to getData from the current step. I mean call some generic method on each step child component, like leaveStep. Then leaveStep returns some data, which I will pass to redux action.
If I got it right, the ideal solution would be lifting state up to the Parent component, take a look at this part of the React documentation. But since you don't have control of your components and it may create you some problems to sync the states. Something like this will do the trick:
class Parent extends Component {
state = {
childData: null
}
getChildData = (data) => {
this.setState({
childData: data,
}, () => { console.log(this.state); });
}
render() {
return <Child setData={this.getChildData} />
}
}
class Child extends Component {
state = {
data: 'this is child data'
}
render() {
return <button onClick={() => this.props.setData(this.state.data)}>Next</button>;
}
}
But remember that this creates a duplicate state, breaking the single source of truth and can be very messy for large applications.
Related
I have a parent component with multiple identical children of which only one can be active at a time. The active state is to be set through an internal event on the child itself e.g. a button click, and not by the parent. I want the active state to be unset by a call from a sibling but I cant find a way for siblings to call eachother's methods. Ive tried refs but they are only accessible from the parent and i cant find a way to make a child ref available within itself without maintaining a list of refs on the parent which i dont want as i only need to store the currently active one.
Simple example
e.g.
<Parent>
<Child active={false}/>
<Child active={false}/>
<Child active={true}/>
</Parent>
where a child is something like
export class Child extends React.Component {
constructor() {
super(props);
state = {
active: props.active;
}
}
setActive(active) {
setState ({active : active});
}
onclick = () => {
// get currently active other sibling?
// call setActive direct on other sibling.
// e.g. other.setActive(false);
// set current to active using the same method
this.setActive(true);
}
render() {
return (
<button onclick={this.onclick}/>
<p>current state={this.state.active ? "active": "inactive"}
);
}
}
I've tried passing in parent setActiveRef and getActiveRef functions as props to the children to maintain a single shared ref (the active one) and use getActiveRef().current.setActive directly but i cant find a way to access the ref of a child component from within itself to send to the setActiveRef() on the parent in the first place.
any advice much appreciated. thanks
In short, this isn't how React is designed - children won't be able to modify each other's state directly. A simple analogy would be a literal parent and their children. You want the children to know when it's time to raise their hand, but they don't take directions from each other, only from Mom or Dad. What you CAN do is tell the kids how to communicate with their parents, and let their parents deal with it.
I'm sure there are better ways, but here is some code:
export class Parent extends React.Component {
constructor() {
super(props);
state = {
activeChild: "Jimmy"
}
};
// Let the parents listen for requests
handleChildRequest (name) => {
this.setState({activeChild: name});
};
render() {
<div>
<Child active={this.state.activeChild === "Jimmy"} name="Jimmy" handleRequest={this.handleChildRequest} />
<Child active={this.state.activeChild === "Sally"} name="Sally" handleRequest={this.handleChildRequest} />
<Child active={this.state.activeChild === "Fred"} name="Fred" handleRequest={this.handleChildRequest} />
</div>
};
}
export class Child extends React.Component {
constructor() {
super(props);
// Lets forget about local state, we don't need it!
}
onclick = () => {
this.props.handleRequest(this.props.name);
}
render() {
return (
<button onclick={this.onclick}/>
<p>current state={this.props.active ? "active": "inactive"}
);
}
}
Answer to my own question using component passing (this) references to parent callback. This is not complete code but i illustrates the point. Appears to work fine for my use case (updating realtime map locations) which is more complex than this simplified example.
parent component passes callbacks to children to store ref to active component
export class Parent extends React.Component {
activeChild = undefined;
setActiveChild = (child) => {
activeChild = child;
}
getActiveChild = () => {
return activeChild;
}
// set up some callback props on each child
render() {
return (
<Parent>
<Child active={false} setActiveChild={setActiveChild} getActiveChild={getActiveChild}/>
<Child active={false} setActiveChild={setActiveChild} getActiveChild={getActiveChild}/>
<Child active={true} setActiveChild={setActiveChild} getActiveChild={getActiveChild}/>
</Parent>
)
}
each child simply calls back on the parent using prop callbacks and passes itself. this allows the state to be set internally within the component forcing a re-render if values change.
export class Child extends React.Component {
onclick = () => {
// get currently active other sibling stored in parent
let active = this.props.getActiveChild();
// call setActive directly on other sibling.
active.setActive(false);
// store currently active child in parent
this.props.setActiveChild(this);
// set 'this' component to active using the same method
this.setActive(true);
}}
criticisms and improvements most welcome.
thanks
I was looking for a way to do something similar with hooks, and this is what worked for me:
import React, { useState } from 'react';
const Parent = () => {
const [activeChild, setActiveChild] = useState(undefined);
return (
// Now the children have access to the current active child
// and also the ability to change that
<Child activeChild={activeChild} setActiveChild={setActiveChild} id={'1'}/>
<Child activeChild={activeChild} setActiveChild={setActiveChild} id={'2'}/>
)
export default Parent
then inside the Child component...
import React, { useState, useEffect } from 'react';
const Child = ({activeChild, setActiveChild, id}) => {
const [activated, setActivated] = useState(false);
useEffect(() => {
if (activeChild !== id) {
setActivated(false);
}
}, [activeChild, chapter]);
return (
//on click the component will be able to be activated and set itself as the activated component.
<div onClick={()=> {
setActivated(true);
setActiveChild(id)
}} />
)
export default Child
The useEffect in the Child will check if its id (which is unique to itself) is the id of the activeChild. If not, it'll make sure that its local state of activated is false.
Once activated though, it'll set its local state of activated to true, and set the activeChild's id to its own id.
any feedback is welcome!! This made sense in my head.
Let's supposed you have an array of childs components, each one of those child, will have a prop called active, so you could use an state variable to store the array of childs, so when one of the childs gets updated, and cause a rerender of each one of the child components as well.
import React from "react";
const Parent = () => {
const childs = [{ active: false }, { active: false }, { active: false }];
const [childrens, setChildrens] = React.useState(childs);
const onOptionSelected = idx => {
setChildrens(prevOptions =>
prevOptions.map((opt, id) => {
opt.active = id === idx;
return opt;
})
);
};
return (
<div>
{childrens.map((child, id) => {
return (
<Child
key={id}
id={id}
active={child.active}
onOptionSelected={onOptionSelected}
/>
);
})}
</div>
);
};
const Child = ({ id, active, onOptionSelected }) => {
console.log(id)
const onClick = () => {
onOptionSelected(id);
};
return (
<>
<button onClick={onClick}>set active</button>
<p>current state={active ? "active" : "inactive"}</p>
</>
);
};
export default Parent;
Every row in my SideMenuContainer corresponds to an object from schema.json, showing only the name property. The behavior I want is that when a row is clicked, the PaneViewContainer toggles to display the name and other properties of that respective object from the json.
In App.js, the data is passed to SideMenuContainer like so:
render() {
return (
<MainContainer>
<SideMenuContainer genInfoList={this.state.genInfoList}/>
<PaneViewContainer genInfoList={this.state.genInfoList}/>
</MainContainer>
);
}
In SideMenuContainer, every row is populated like this:
render() {
return (
<SideMenuPane>
<SideMenu>
<div>
<h2>GenInfo</h2>
{this.props.genInfoList.map(genInfoElement => {
return (
<SideMenuRow>
{genInfoElement.name}
</SideMenuRow>
);
})}
</div>
</SideMenu>
</SideMenuPane>
);
}
What I want to do is change the genInfoList information being displayed in the PaneViewContainer based on which row is clicked in its sibling, SideMenuContainer.
The entire genInfoList data is being passed to both sibling components from their parent App.js, so I want to change which portion of that data is loaded in the Pane based on the row clicked in the SideMenu.
I thought about using the Context API, but I'm having trouble figuring out how to implement it for this purpose. Any ideas?
If I understand correctly you have your information stored in the parent element of both components then you can just pass a function down as a prop and have all of your logic stored in the parent element.
changeInfoList = id => {
//change your info list based on id or index or whatever
this.setState({
//your new list
})
}
render() {
return (
<MainContainer>
<SideMenuContainer changeInfoList={this.changeInfoList} genInfoList={this.state.genInfoList}/>
<PaneViewContainer genInfoList={this.state.genInfoList}/>
</MainContainer>
);
}
and then call changeInfoList from your component with props
render() {
return (
<SideMenuPane>
<SideMenu>
<div>
<h2>GenInfo</h2>
{this.props.genInfoList.map(genInfoElement => {
return (
<SideMenuRow>
{genInfoElement.name}
<button onClick={this.props.changeInfoList(genInfoElement.id)>CLick Me</button>
</SideMenuRow>
);
})}
</div>
</SideMenu>
</SideMenuPane>
);
}
this is commonplace in react as you should have smart components and dumb components. When you have components not in the same tree or spread far away then the context api is very useful. In your case I don't think its necessary.
Without external state management, you would have to pass down a callback (as props), so the children can update the parent's state.
As the components get far away from each other, this pattern can get annoying (passing down callbacks each time). That's where external state management can help.
Here's a simple (and untested) example using a callback:
class Counter extends React.Component {
constructor() {
super();
this.increment = this.increment.bind(this);
this.state = {count: 0};
}
increment() {
let count = thist.state.count;
this.setState({count: count + 1});
}
render() {
return <div>
<CounterButton increment={this.increment}/>
<CounterDisplay count={this.state.count}/>
</div>;
}
}
class CounterButton extends React.Component {
render() {
let increment = this.props.increment;
return <button onClick={increment}>Plus One</button>;
}
}
class CounterDisplay extends React.Component {
render() {
let count = this.props.count;
return <span>{count}</span>;
}
}
I am trying to get data from parent component. I know that I can pass in functions or variables to the child component but its looks cleaner to just pass in this to child component to access to its parent functions or variables.
class Parent extends Component {
state = {
_id: 123456789
}
foo = (val) => {
this.setState({_id:val})
}
render() {
return(
<Child parentInstance={this} />
)
}
}
class Child extends Component {
update = (e) => {
this.props.parentInstance.foo(e.target.value);
}
render() {
return(
<p>{this.props.parentInstance.state._id}</p>
<input onChange={this.update} />
)
}
}
If you find yourself passing this down (as I did many times when learning React!), you’ll find it useful to re-read Thinking in React.
In this instance, you should be passing the ID down as a prop. When the state changes, React knows that it’s used to calculate that prop and so will re-render the child component.
As for the callback, that’s best handled as an individual function. If you find yourself passing many callbacks around in your app, you may want to look into using a more complex state management system than just Reacts internal state values.
class Parent extends Component {
state = {
_id: 123456789
}
foo = (val) => {
this.setState({ _id: val })
}
render() {
return(
<Child parentId={this.state._id} onChange={this.foo} />
)
}
}
class Child extends Component {
update = (e) => {
this.props.onChange(e.target.value);
}
render() {
return(
<p>{this.props.parentId}</p>
<input onChange={this.update} />
)
}
}
It is more typical to pass properties and callbacks as props, not the instance itself.
i.e. <Child parentId={this.state._id} onUpdate={this.foo} />
I have 3 components, that is 2 child components and one parent component.
I wanted to pass the child component value to parent (only the values not the components enitirely,it should not visible in parent) and this values from parent to another child component.
Any suggestions or logic of how to proceed on this, since I don't have any guidance as of right now I had to ask here. Is the above problem possible.
The code is very complex, so I have not put here.
Thank you
When you say values, do you mean state, props, user input, something else?
If you mean state or props: React has a 1-way data flow, so the easiest way to accomplish this is to actually store the data at a higher level. Either store the data used by the child in the parent and pass it down to the children for consumption, or else use a store that both parent and children have access to. Either way, this will make it much easier for all components to access the data.
If you mean user input: one way you can accomplish this is to pass a callback from the parent component to the child as a prop, and then in the child call that callback when a user does something or changes some value. The callback function can make the data accessible to the parent on that user action, and then you can decide what to do with the data from there.
AksharaDL,
Child to Parent — Use a callback and states
Parent to Child — Use Prop
Also here is another article explaining it: https://medium.com/#ruthmpardee/passing-data-between-react-components-103ad82ebd17
here is the solution.
in parrent component you have a state. and have a setData method to update state. pass setData to ChildOne use props. and data to ChilTwo and use it
class StoreBanner extends React.Component {
constructor() {
this.state = {
data: 'whatever'
}
}
setData = (data) => {
this.setState({data})
}
render() {
return (
<div>
<ChildOne setData={this.setData}/>
<ChildTwo data={this.state.data}/>
</div>
)
}
}
and in ChildOne you can update the parrent state
class ChildOne extends React.Component {
setParentData = () => {
this.props.setData('another')
}
...
}
You can do it like this.
class ParentComp extends React.Component {
constructor() {
super();
this.state = {
theStateToPass: null
}
this.receiveDataFromChild = this.receiveDataFromChild.bind(this);
}
receiveDataFromChild(data) {
this.setState({
theStateToPass: data
})
}
render() {
return (
<div>
<FirstChild updateMe={this.receiveDataFromChild} />
<SecondChild stateFromFirstChild={this.state.theStateToPass} />
</div>
)
}
}
class FirstChild extends React.Component {
constructor(props) {
super(props);
this.callParentMethod = this.callParentMethod.bind(this);
}
callParentMethod(e) {
let someDataToSend = "ABC";
this.props.updateMe(someDataToSend)
}
render() {
return (
<div onClick={this.callParentMethod}>
</div>
)
}
}
class SecondChild extends React.Component {
render() {
return (
<div>
{this.props.stateFromFirstChild}
</div>
)
}
}
however it becomes complex and might lead to one's pulling their hair out. so i would suggest using redux , it keeps the flow simple , you have a reducer , actions and a container. everything goes with a flow and keeps it clean but it does comes with an extra overhead of more code as you will be creating container reducer and actions.
I am developing a simple browser app to get some specific data from the user.
I have several components to simplify the proccess of collecting that data, but I am currently rethinking my way of rendering this component.
Basically, i have my main component, which uses state to keep track of which component to render.
I am doing this for almost all of my components.
Also, i also have a function inside the parent component, that i pass to the child component via props, and that is used as a callback to pass the child state to its parent, when that component is no longer useful.
class Main extends React.Component {
constructor(props){
this.state = {
renderA: true,
renderB: false,
childState: null
}
}
collectState(state){
this.setState({
childState: state
});
}
render() {
let content = null;
if(this.state.renderA === true){
content = <ComponentA />
} else {
content = <ComponentB />
}
return(
<div>
{content}
</div>);
}
}
So, using the above example, the child would be something like this
class ComponentA extends React.Component {
constructor(props){
super(props);
this.state = {
stop: false,
usefullInfo: null
}
destroy() {
this.props.collectstate(this.state.usefullInfo)
}
render(){
render something (like a Form) untill this.state.usefullInfo is set;
in that case, set this.state.stop true which will call destroy, passing the usefull information to parent
}
}
So, this method works for me, but i can see clearly that most probably this is not the way to do this.
At this point my question are:
1) how can I stop rendering a component without having to track it with some property like this.state.stop ?
2) if i want to render 2 different components, like in the main component, do I always have to keep a renderA and renderB property on state, to render one or another?
3) is there a better way to pass information from child to parent? i am currently using a callback function passed via props from parent to child, and i am invoking that callback when the component has done its purpose
4) any general suggestions on how to improve the quality of the above code?
Thank you for you help :)!
Your example works fine, but in React it is recommended to lift state up when handling data from multiple children (source). So I would recommend to keep the sate of every children in the parent, and pass props with values and handlers to the children.
Here's a sample app you can check. The form components handle the case you want to implement.
To answer your questions:
The parent component should decide, based on its own state, whether to render a child component or not.
It's not needed to keep variables on state about what component to render. that should be computed in render() based on the parent's state
Yes, callback are the recommended way to pass information to parents
Code quality looks good. You can always do good with tools like prettier or ESlint.
Here's an example:
class Main extends React.Component {
constructor(props) {
this.state = {
stateA: '',
stateB: '',
};
}
handleStateChange(name, value) {
this.setState({
[name]: value,
});
}
render() {
const { stateA, stateB } = this.statel;
const shouldRenderA = !stateA;
if (shouldRenderA) {
return <ComponentA value={stateA} onChange={value => this.handleStateChange('stateA', value)} />;
}
return <ComponentB value={stateA} onChange={value => this.handleStateChange('stateB', value)} />;
}
}
class ComponentA extends React.Component {
render() {
const { value, onChange } = this.props;
return <input type="text" value="value" onChange={onChange} />;
}
}