Parameterizing rendering of parent component from child - reactjs

I have a Parent component that contains a Child component. Suppose that the Parent wants to ask the Child its preferred background color and uses it to modify how the Parent renders itself:
class Child extends React.Component {
favoriteColor = () => "red";
render = () => (<span>Hello world</span>);
}
class Parent extends React.Component {
render = () => {
const child = (<Child />);
return (
<div style={{backgroundColor: child.favoriteColor()}}>
{child}
</div>
);
};
}
What is the idiomatic way to do this in React?
In my real project, I have a Parent component that has a dynamic set of children. The Parent wraps each child in a React Bootstrap <Col>. But sometimes a child does not want to be rendered, and the correct thing would be to omit the <Col>. So the Parent component needs to be able to ask each child "do you have anything to render?"

One way to do this depending on the circumstances is to declare Child.favoriteColor as static. As a static function it has no access to the instance's props or state.
class Child extends React.Component {
static favoriteColor = () => "red";
render = () => (<span>Hello world</span>);
}
class Parent extends React.Component {
render = () => {
return (
<div style={{backgroundColor: Child.favoriteColor()}}>
<Child />
</div>
);
};
}

Related

React.js: how to set an active flag in one of multiple identical children?

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;

Learner question: How to pass data from one class to another

I am learning spfx dev. I am creating a form with several different classes to learn how they can interact and pass data between each other.
I have two separate classes. One Parent class has a submit button which uses the Parents state to submit to a SharePoint list.
The other class component has it's own set of states and fields. I want whatever is entered by the user in the child component, to be submittable(!) by the parent class.
Here's my submit function:
private _onSubmit() {
this.setState({
FormStatus: 'Submitted',
SubmittedLblVis: true,
}, () => {
pnp.sp.web.lists.getByTitle("JobEvaluationItems").items.add({
JobTitle: this.state.JobTitle,
Faculty: this.state.Faculty,
Department: this.state.SelectedDept,
SubDepartment: this.state.SubDepartment,
DeptContactId: this.state.DeptContact,
FormStatus: this.state.FormStatus,
EvalType: this.state.EvalType,
JobTitReportTo: this.state.JobTitReportTo
}).then((iar: ItemAddResult) => {
let list = pnp.sp.web.lists.getByTitle("JobEvaluationItems");
list.items.getById(iar.data.Id).update({
JobRef: 'JE'+iar.data.Id
});
this.setState({
JobRef: iar.data.Id
});
});
});
}
Here is a function from the child component which handles whatever is typed into a field:
private _onJobTitReportToChange = (ev: React.FormEvent<HTMLInputElement>, newValue?: string) => {
this.setState({
JobTitReportTo: newValue
});
}
How would I pass the state function above (which is held within the child component) to the Parent component?
class Child extends React.Component {
state = {
childValue: 1
}
onChange = e => {
this.setState({childValue: e.target.value}, () => {
this.props.onChange(this.state);
})
}
render () {
return <input value={this.state.childValue} onChange={this.onChange} />
}
}
class Parent extends React.Component {
state = {
parentValue: 123,
dataFromChild: null
}
handleChildChange = childData => {
this.setState({dataFromChild: childData});
}
render () {
return (
<div>
<Child onChange={this.handleChildChange} />
<pre>{JSON.stringify(this.state, null, 2)}</pre>
</div>
)
}
}
ReactDOM.render(<Parent />, document.querySelector("#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>
In React world are two common used ways to transfer data:
If you want to pass it down to the child component - use props;
If you want to pass it up to the parent component - use callback;
There is another way - Context, but it's a whole different story.
if you want to pass data from one component to other.Follow the below steps.
1.PARENT --> CHILD
In parent component's render
render(){
return (
<ChildComponent data1={} data2={}/>
)
}
2.CHILD-->PARENT
make a handler in your submit function which is received to this child component from props
//CHILD COMPONENT
onSubmit=()=>{
...
//some data
...
this.props.onSubmit(data)
}
//Parent component
render(){
return(
....
<ChildComponent onSubmit={this.onSubmit}/>
....
)
}
How would I pass the state function above (which is held within the child component) to the Parent component?
It's one of React's concepts called lifting state up.
class Parent extends React.Component {
const someFunction = () => {} // move the function to the parent
render() {
return (
<>
<ChildComponent someFunction={someFunction} /> // pass function down to child
</>
)
}
}
const ChildComponent = (props) => {
return <Button onClick={props.someFunction} /> // use parent function
}

How to pass callback functions with parameters from parent to child components?

I'm really new to ReactJS framework.
I'm trying to pass a callback function from parent to child component.
What i'm trying to pass is a callback function (changeState) to a child component. The child component is a fabric-ui button and this function will be called when clicking on the button.
Parent code : only the necessary code
public constructor(props: {}) {
super(props);
this.state = {
columns: [],
hasError:false,
sortedItems: []
};
this.changeState = this.changeState.bind(this);
}
public changeState = (itemId:React.ReactText)=>{
const resolvedKey='Status';
const idKey='Status';
const item = this.state.sortedItems!.filter(ite => ite[idKey] === itemId)[0];
item[resolvedKey] = 'Résolu';
}
<ResolveButton disabled={isResolved} uniqueId={fieldContent} changeState={this.changeState } />
Child code
import { PrimaryButton } from 'office-ui-fabric-react/lib/Button';
import * as React from 'react';
export interface IHandleChange {
changeState: (itemId:React.ReactText)=>void;
disabled:boolean;
uniqueId:string| number;
}
export class ResolveButton extends React.Component<IHandleChange, {}> {
constructor(props:any) {
super(props);
}
public handleClick = () => {
this.props.changeState(this.props.uniqueId);
alert(`Item ${this.props.uniqueId}`);
}
public render(): JSX.Element {
return (
<div>
{
!this.props.disabled &&
<PrimaryButton
data-automation-id="test"
text="Résolu"
onClick={this.handleClick}
allowDisabledFocus={true}
/>
}
</div>
);
}
}
export default ResolveButton;
The problem is that the child component is not even created when i execute the code above. However the child component is created when i don't pass the callback function (changestate).
This has nothing to do with the parameters disabled and uniqueId passed to the child components. what i'm trying to say is that if i try to remove changeState in the child node interface the child component is well created, however once i introduce changeState in the interface and try to pass it from the parent component the child is never shown in the DOM.
If isResolved variable is not part of the state in parent component, its change will not re-render the tree. It might be the problem.

Trigger a function that is inside the child component from the parent

How can I trigger a function that is inside the child component from the parent component doing it in the same style as drawer navigation.
They do it like this: this.props.navigation.toggleDrawer(); from the parent
How can I do the same?
If I understood your question correctly, I think you mixed up thing a bit. The example you are showing is an example for triggering a parent component's function from child.
I'll try to clear things up a bit with 2 examples.
1) Trigger from child:
To trigger a function of a parent component from a child you can just pass the function as a property to the child component and run it when you need it.
class Parent extends React.Component {
someFunction = (text) => {
console.log('Message from child: ', text);
}
render () {
return(
<Child someProperty={this.someFunction} />
)
}
}
class Child extends React.Component {
_onPress = () => {
// check if the property is defined and not null
if(this.props.someProperty) {
// run the function that is passed from the parent
this.props.someProperty();
}
}
render() {
return(
<Button onPress={this._onPress} title="Click Me"/>
)
}
}
2) Trigger from parent:
To trigger a function on a child component from parent, you can pass a property that changes when some action happens on the parent component. This will trigger a re-render (in most cases, for more info please take a look at shouldComponentUpdate) in child component. You can check the property changes and then do what you need to do in child component.
class Parent extends React.Component {
state = {
someParameter: 'someInitialValue',
}
someFunction = (text) => {
this.setState({ someParameter: 'someValue' });
}
render () {
return(
<Child someProperty={this.state.someParameter} />
)
}
}
class Child extends React.Component {
someFunction = (text) => {
console.log('Message from parent: ', text);
}
componentDidUpdate(prevProps, prevState, snapshot) {
// Check if the suplied props is changed
if(prevProps.someProperty !== this.props.someProperty) {
// run the function with the suplied new property
this.someFunction(this.props.someProperty);
}
}
render() {
return(
{/* ... */}
)
}
}
Since you haven't provided any code. Here are my thoughts on the problem.
When you list down a component in a navigator, be it StackNavigator or DrawerNavigator, the component will receive some props provided by the navigation class itself.
There's an option to send more parameters as props to these navigation objects. Among these extra parameters, can be your one method toggleDrawer().
Also, if you parent component is listed in the navigator and the child component is not. You'll need to explicitly pass the navigation props (this.props.navigation) to the child component.
So, when you are inside that child component, all you gotta do is fetch those props and voila, it'll do the needful!
Hope this clarifies out stuff for you.
EDIT --- For your third comment
Assumptions:
Parent component is listed as DrawerNavigator({ParentScreen: {screen: ParentScreen}})
There is a <Route/> component in ParentScreen.
So, what you can do is pass the default navigation props to the <Route> component.
Like - <Route navigation={this.props.navigation} /> and in child component, you can trigger this.props.navigation.toggleDrawer() on any onPress() event of any element.
class Parent extends React.Component {
parentFunction() {
this.refs.chid.childFunction(parameterToPassed);
}
render () {
return(
{/* ... */}
<Child ref='child' />
{/* ... */}
)
}
}
class Child extends React.Component {
childFunction(text){
console.log('parameter Passed from parent: ', text);
}
render() {
return(
{/* ... */}
)
}
}
Function named as childFunction is declared in child component. and called in parent component, inside the function parentFunction.
for more information Call child function from parent component in React Native

Lifting up state in React

say i have this React Class. This is NOT my main component that I'm rendering. how can i pass the state i set in here UPWARDS to the parent component.
class Player extends React.Component {
constructor(props) {
super(props);
this.state = {
playerOneName: ''
}
this.selectPlayerOne = this.selectPlayerOne.bind(this);
}
selectPlayerOne(e, data) {
this.setState({playerOneName: data.value})
}
render() {
let players = [];
this.props.players.map((player) => {
players.push({text: player.name, value: player.name})
})
return (
<div className="playersContainer">
<div className="players">
<Dropdown onChange={this.selectPlayerOne} placeholder='Select Player One' fluid selection options={players} />
</div>
</div>
)
}
}
when I say parent component i mean the class that is going to display player like so:
<Player />
I.e. how can I make this.state.playerOneName available to the parent?
Hey so the point here is that you are basically passing in, from your parent component into your child component, a prop which is a function.
In parent:
handler(newValue){
this.setState({key: newValue})
}
render() {
return(
<Child propName={handler.bind(this)}>
)
}
When a change takes place in your child component, you call the function and pass in the new value as an input. This way you are calling the function in your child component, and making a change to the state of the parent component.
In your case you want to, instead of setting the state of playerOneName, pass in a function from the parent of this class and do something like this.props.functionFromParent(playerOneName) in your 'selectPlayOne' function.
It is true that the flux pattern is unidirectional, however when you're dealing with smart and dumb components you will see that data is passed from a dumb component to a smart component in this way. It is perfectly acceptable React form!
"Lifting state up" for React means that you should replace data from your child component to parent component state and pass data for presentation to child component by props.
You can do something like this to achieve your goal. Create a parent component which hold playerOneName as its state. Pass it as a prop to child component and with that also a function that changes the playerOneName whenever it is changed in the child component.
class Parent extends Component {
constructor(props){
this.state = {
playerOneName: ''
}
}
render() {
return(
<Child
playerOneName={this.state.playerOneName}
onPlayerOneNameChange={(playerOneName) => this.setState({playerOneName})}
/>
);
}
}
Use this function like this in child component to change the name of playerOneName in Parent component, like this your child component is only displaying the value of the playerOneName all the changes are done in Parent component only.
class Child = props => {
const { playerOneName, onPlayerOneNameChange } = props;
return (
<TextInput
value={playerOneName}
onChangeText={(playerOneName) => onPlayerOneNameChange(playerOneName)}
/>
);
}
By this you can use updated playerOneName in your Parent component whenever you like by using this.state.playerOneName

Resources