add prop value and property to all children - reactjs

I want to pass a prop value to all of a components current children, however I feel it's a little tedious to do it to each and every single child component (especially if you have a considerable amount). I initially thought a context would suffice, but I came across the same issue of adding a considerable amount of boilerplate code that could otherwise be extremely simple.
Is there anything within React that could achieve the same affect as:
<Parent>
<ChildOne propForAllChildren={this.state.example} />
<ChildTwo propForAllChildren={this.state.example} />
<ChildThree propForAllChildren={this.state.example} />
</Parent>
With something like this:
<Parent passPropsToChildren={{"propForAllChildren": this.state.example}}>
<ChildOne />
<ChildTwo />
<ChildThree />
</Parent>
There's nothing in the documentation which I can find, and I could very easily create a component which would do exactly this. But, why reinvent the wheel if it already exists?

In this case I usually look the problem on children side. Let me explain better: you know that all Child will have a prop called propForAllChildren and most probably this prop is always at the same value. Well, on Child put a default value for that prop so you could avoid to pass it:
<Parent>
<ChildOne />
<ChildTwo />
<ChildThree />
</Parent>
export default function ChildOne(props) {
const { propForAllChildren = true } = props;
...
}
And the same thing for the other Child.
Yes, this line const { propForAllChildren = true } = props; will be the bolierplate but I think is a minimum price to pay.

hi this is the solution
import React,{useState , useEffect} from 'react';
const ChildOne = (props) => {
console.log(props);
return(
<p> ChildOne </p>
)
}
const ChildTwo = (props) => {
console.log(props);
return(
<p> ChildTwo </p>
)
}
const ChildThre = (props) => {
console.log(props);
return(
<p> ChildThre </p>
)
}
const Parent = ({ children,passPropsToChildren }) => {
const [ propedChilds , setPropedChilds ] = useState([]);
useEffect(() => {
if(passPropsToChildren){
const childrenWithProps = React.Children.map(children, child => {
if (React.isValidElement(child)) {
return React.cloneElement(child, {
...passPropsToChildren
});
}
return child;
});
setPropedChilds(childrenWithProps);
}
}, [children,passPropsToChildren]);
return(
<>
{propedChilds}
</>
)
}
const App = () => {
return(
<Parent passPropsToChildren={{ "propForAllChildren": true }}>
<ChildOne />
<ChildTwo />
<ChildThre />
</Parent >
)
}

As it stands, there seems to be no official iterable prop component such as the one defined. So I went to my own devices to create a very basic implementation of one - just in case anyone deems it useful - feel free to use or alter.
Hopefully one day the React team will add similar functionality to a future version of React.
const PropsToChildren = React.memo(({ props, children }) => (
<Fragment>
{ children.map((child) => React.cloneElement(child, {...props})) }
</Fragment>
));
// it can be used as such
<PropsToChildren props={{propA: propAValue}}>
{ /* children go here... */ }
</PropsToChildren>
// and it will output the following
<ChildOne propA={propAValue} />
<ChildTwo propA={propAValue} />
// etc...

You can create a HOC DispatchPropsToChildren to do this functionality. The purpose of this HOC component is to loop between children and injecting to them the props.
function DispatchPropsToChildren({ propsToDispatch, children }) {
return (
<>{children.map(child => cloneElement(child, { ...propsToDispatch }))}</>
);
}
How to use it:
Component App:
export default function App() {
return (
<Parent>
<DispatchPropsToChildren propsToDispatch={{ propOne: 1, propTwo: 2 }}>
<ChildOne />
<ChildTwo />
</DispatchPropsToChildren>
</Parent>
);
}
Component Parent:
function Parent({ children }) {
return children;
}
Component ChildOne:
function ChildOne(props) {
console.log(props);
return <div>ChildOne</div>;
}
Component ChildTwo:
function ChildTwo(props) {
console.log(props);
return <div>ChildTwo</div>;
}
You can check this demo:
https://stackblitz.com/edit/react-xqmevc

Related

React Pass Array Through Props

I have an array that I want to pass to a grandchild component.
class App extends Component {
constructor(props) {
super(props);
...
}
componentDidMount() {
array = ['a', 'b', 'c'];
}
render() {
return (
<Child arrayData={this.props.arrayData} /> ???
)}
I know that I have to pass the data to the child first (and then to the grandchild from there) via a prop but how do I format it? And how does the child receive the prop?
Here is an example of passing a value from an array from parent to grandchild. Notice how salutations is declared and assigned in the in the parent prop and propagated through the child to the grandchild.
This example be amended easily to pass the entire array instead of a single value within it.
render() {
return (
<div>
<h1>Passing props through the component tree...</h1>
<Parent greeting={salutations[0]}/>
</div>
);
}
}
const Parent = (props) => {
return (
<div>
<h2>Parent</h2>
<Child greeting={props.greeting} />
</div>
);
};
const Child = (props) => {
return (
<div>
<h3>Child</h3>
<Grandchild greeting={props.greeting} />
</div>
);
};
let salutations = ["hey", "what's up", "hello"]
const Grandchild = (props) => <h4>Grandchild - {props.greeting}</h4>;
ReactDOM.render(<App />, document.getElementById('app'));
Hello
Dear, You can try to use this code
import ClientImg1 from "../../assets/static/img/client/client_img_1.png";
import ClientImg2 from "../../assets/static/img/client/client_img_2.png";
const themes = {ClientImg1, ClientImg2};
render() {
return (
<Child arrayData1={themes.ClientImg1} arrayData2={themes.ClientImg2}/>
)
}
and
in the
<Child arrayData1={themes.ClientImg1} arrayData2={themes.ClientImg2}/>
component or function page
you can call them by using this
if function
function ClientSingleItem({arrayData1,arrayData2}) {
return (
<img src={arrayData1} alt="ClientImg" />
<img src={arrayData2} alt="ClientImg" />
.....
)
}

React Hook : Send data from child to parent component

I'm looking for the easiest solution to pass data from a child component to his parent.
I've heard about using Context, pass trough properties or update props, but I don't know which one is the best solution.
I'm building an admin interface, with a PageComponent that contains a ChildComponent with a table where I can select multiple line. I want to send to my parent PageComponent the number of line I've selected in my ChildComponent.
Something like that :
PageComponent :
<div className="App">
<EnhancedTable />
<h2>count 0</h2>
(count should be updated from child)
</div>
ChildComponent :
const EnhancedTable = () => {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Click me {count}
</button>
)
};
I'm sure it's a pretty simple thing to do, I don't want to use redux for that.
A common technique for these situations is to lift the state up to the first common ancestor of all the components that needs to use the state (i.e. the PageComponent in this case) and pass down the state and state-altering functions to the child components as props.
Example
const { useState } = React;
function PageComponent() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1)
}
return (
<div className="App">
<ChildComponent onClick={increment} count={count} />
<h2>count {count}</h2>
(count should be updated from child)
</div>
);
}
const ChildComponent = ({ onClick, count }) => {
return (
<button onClick={onClick}>
Click me {count}
</button>
)
};
ReactDOM.render(<PageComponent />, document.getElementById("root"));
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
You can create a method in your parent component, pass it to child component and call it from props every time child's state changes, keeping the state in child component.
const EnhancedTable = ({ parentCallback }) => {
const [count, setCount] = useState(0);
return (
<button onClick={() => {
const newValue = count + 1;
setCount(newValue);
parentCallback(newValue);
}}>
Click me {count}
</button>
)
};
class PageComponent extends React.Component {
callback = (count) => {
// do something with value in parent component, like save to state
}
render() {
return (
<div className="App">
<EnhancedTable parentCallback={this.callback} />
<h2>count 0</h2>
(count should be updated from child)
</div>
)
}
}
To make things super simple you can actually share state setters to children and now they have the access to set the state of its parent.
example:
Assume there are 4 components as below,
function App() {
return (
<div className="App">
<GrandParent />
</div>
);
}
const GrandParent = () => {
const [name, setName] = useState("i'm Grand Parent");
return (
<>
<div>{name}</div>
<Parent setName={setName} />
</>
);
};
const Parent = params => {
return (
<>
<button onClick={() => params.setName("i'm from Parent")}>
from Parent
</button>
<Child setName={params.setName} />
</>
);
};
const Child = params => {
return (
<>
<button onClick={() => params.setName("i'm from Child")}>
from Child
</button>
</>
);
};
so grandparent component has the actual state and by sharing the setter method (setName) to parent and child, they get the access to change the state of the grandparent.
you can find the working code in below sandbox,
https://codesandbox.io/embed/async-fire-kl197
IF we Have Parent Class Component and Child function component this is how we going to access child component useStates hooks value :--
class parent extends Component() {
constructor(props){
super(props)
this.ChildComponentRef = React.createRef()
}
render(){
console.log(' check child stateValue: ',
this.ChildComponentRef.current.info);
return (<> <ChildComponent ref={this.ChildComponentRef} /> </>)
}
}
Child Component we would create using
React.forwardRef((props, ref) => (<></>))
. and
useImperativeHandle(ref, createHandle, [deps])
to customizes the instance value that is exposed to parent components
const childComponent = React.forwardRef((props, ref) => {
const [info, setInfo] = useState("")
useEffect(() => {
axios.get("someUrl").then((data)=>setInfo(data))
})
useImperativeHandle(ref, () => {
return {
info: info
}
})
return (<> <h2> Child Component <h2> </>)
})
I had to do this in type script. The object-oriented aspect would need the dev to add this callback method as a field in the interface after inheriting from parent and the type of this prop would be Function. I found this cool!
Here's an another example of how we can pass state directly to the parent.
I modified a component example from react-select library which is a CreatableSelect component. The component was originally developed as class based component, I turned it into a functional component and changed state manipulation algorithm.
import React, {KeyboardEventHandler} from 'react';
import CreatableSelect from 'react-select/creatable';
import { ActionMeta, OnChangeValue } from 'react-select';
const MultiSelectTextInput = (props) => {
const components = {
DropdownIndicator: null,
};
interface Option {
readonly label: string;
readonly value: string;
}
const createOption = (label: string) => ({
label,
value: label,
});
const handleChange = (value: OnChangeValue<Option, true>, actionMeta: ActionMeta<Option>) => {
console.group('Value Changed');
console.log(value);
console.log(`action: ${actionMeta.action}`);
console.groupEnd();
props.setValue(value);
};
const handleInputChange = (inputValue: string) => {
props.setInputValue(inputValue);
};
const handleKeyDown: KeyboardEventHandler<HTMLDivElement> = (event) => {
if (!props.inputValue) return;
switch (event.key) {
case 'Enter':
case 'Tab':
console.group('Value Added');
console.log(props.value);
console.groupEnd();
props.setInputValue('');
props.setValue([...props.value, createOption(props.inputValue)])
event.preventDefault();
}
};
return (
<CreatableSelect
id={props.id}
instanceId={props.id}
className="w-100"
components={components}
inputValue={props.inputValue}
isClearable
isMulti
menuIsOpen={false}
onChange={handleChange}
onInputChange={handleInputChange}
onKeyDown={handleKeyDown}
placeholder="Type something and press enter..."
value={props.value}
/>
);
};
export default MultiSelectTextInput;
I call it from the pages of my next js project like this
import MultiSelectTextInput from "../components/Form/MultiSelect/MultiSelectTextInput";
const NcciLite = () => {
const [value, setValue] = useState<any>([]);
const [inputValue, setInputValue] = useState<any>('');
return (
<React.Fragment>
....
<div className="d-inline-flex col-md-9">
<MultiSelectTextInput
id="codes"
value={value}
setValue={setValue}
inputValue={inputValue}
setInputValue={setInputValue}
/>
</div>
...
</React.Fragment>
);
};
As seen, the component modifies the page's (parent page's) state in which it is called.
I've had to deal with a similar issue, and found another approach, using an object to reference the states between different functions, and in the same file.
import React, { useState } from "react";
let myState = {};
const GrandParent = () => {
const [name, setName] = useState("i'm Grand Parent");
myState.name=name;
myState.setName=setName;
return (
<>
<div>{name}</div>
<Parent />
</>
);
};
export default GrandParent;
const Parent = () => {
return (
<>
<button onClick={() => myState.setName("i'm from Parent")}>
from Parent
</button>
<Child />
</>
);
};
const Child = () => {
return (
<>
<button onClick={() => myState.setName("i'm from Child")}>
from Child
</button>
</>
);
};

How to pass Grandchild data into GrandParent Component without passing data to intermediate components in Reactjs?

i have created three components Aboutus,AboutusChild & GrandChild and now i want to pass grandchild state value in my grandparent component that is "Aboutus" component but without using intermediate component(AboutusChild), is it possible in react js without using redux state management library.
i dont' want to use redux right now until some data-communication concept are not clear.
class AboutUs extends Component {
constructor(props) {
super(props)`enter code here`
this.state = {
childState: false
}
}
myMehtod(value) {
this.setState({
childState: value
})
}
render() {
console.log('AboutUs', this.state.childState)
return (
<div>
<h1 className={`title ${this.state.childState ? 'on' : ''}`}>
About us {this.props.aboutusProp}
</h1>
<AboutUsChild />
</div>
)
}
};
class AboutUsChild extends Component {
myMehtod(value) {
this.setState({
childState: value
},
this.props.myMehtodProp(!this.state.childState)
)
}
render() {
console.log('AboutUsChild', this.state.childState)
return (
<div>
<h1 className={`title ${this.state.childState ? 'on' : ''}`}>About Us Child</h1>
<GrandChild>
without function
</GrandChild>
<h1>About Us Child</h1>
<GrandChild Method={true} myMehtodProp={this.myMehtod.bind(this)} />
</div>
)
}
};
class GrandChild extends Component {
constructor() {
super()
this.state = {
TitleClick: false
}
}
TitleClick() {
this.setState(
{
TitleClick: !this.state.TitleClick
},
this.props.myMehtodProp(!this.state.TitleClick)
)
}
render() {
console.log('GrandChild', this.state.TitleClick)
return (
<div>
{this.props.Method ?
<h1 onClick={this.TitleClick.bind(this)} className={`title ${this.state.TitleClick ? 'on' : ''}`}>Grand Child</h1>
: null}
<h1>{this.props.children}</h1>
</div>
)
}
};
There's really no way to pass child state up without passing it to some callback. But frankly speaking this does not seem like a good design choice to me. You should always have common state on top of all components consuming that state. What you can do, is to pass stuff as children:
class SomeComponent extends React.Component {
...
render() {
const { value } = this.state;
return (
<Child value={value}>
<GrandChild value={value} onChange={this.handleValueChange} />
</Child>
);
}
}
const Child = ({ value, children }) => (
<div>
<h1 className={`title ${value ? 'on' : ''}`}>About Us Child</h1>
{children}
</div>
)
const GrandChild = ({ value, onChange }) => (
<div>
<h1 onClick={onChange} className={`title ${value ? 'on' : ''}`}>Grand Child</h1>
</div>
);
This way you got control from parent component of everything. If this is not the way, because you are already passing children, and for some reason you want to keep it this way, you can pass "render" prop:
// JSX in <SomeComponent /> render function:
<Child
value={value}
grandChild=(<GrandChild value={value} onChange={this.handleValueChange} />)
>
Some other children
</Child>
...
const Child = ({ value, grandChild, children }) => (
<div>
<h1 className={`title ${value ? 'on' : ''}`}>About Us Child</h1>
{grandChild}
{children}
</div>
)
If you want to be more fancy and there will more than few levels of nesting, you can always use context (highly recommend reading docs before using):
const someContext = React.createContext({ value: true, onChange: () => {} });
class SomeComponent extends React.Component {
...
render() {
const { value } = this.state;
return (
<someContext.Provider value={{ value: value, onChange: this.handleValueChange }}>
<Children>
</someContext.Provider>
);
}
}
...
const SomeDeeplyNestedChildren = () => (
<someContext.Consumer>
{({ value, onChange }) => (
<h1 onClick={onChange}>{value}</h1>
)}
</someContext.Consumer>
)
I would pick first two, if your structure is not that complex, but if you are passing props deeply, use context.
The only way to do something of this sort without external library would be leveraging React's Context API, although bare in mind that it is not nearly as robust as redux or mobX.

Which would be better: use dispatch on many components or collect data and then dispatch it all?

I have components
<Component>
<ChildComponent1 />
<ChildComponent2 />
<ChildComponent3 />
<ChildComponent4 />
<ConfirmButton />
</Component>
Child components look like:
#connect(data => data)
class ChildComponent1 {
change() {
this.props.dispatch(action.someAction());//it dispatch to store
}
render() {
return <select onChange={this.change}>{render select}</select>
}
}
Child components value dispatches on each select value change, but i do nothing until I have collected data from all child components.
Maybe it would be better dispatch all data after collecting it.
render(
return <Component>
<ChildComponent1 onChange={(data) => this.setState({data})} /> //uses arrow function in this place it is bad, but it is example
<ChildComponent2 onChange={(data1) => this.setState({data1})} />
<ChildComponent3 onChange={(data2) => this.setState({data2})} />
<ChildComponent4 onChange={(data3) => this.setState({data3})} />
<ConfirmButton onApply={() => this.props.dispatch(this.getState())} />
</Component>
)
What is better approach ?
Here's how I would do it, I'm assuming you're using babel to traspile JS, and that your actions are defined in './redux/actions'.
You're probably using "babel-plugin-transform-decorators-legacy", but to run this code you need to add "transform-class-properties"
import actions from './redux/actions';
const mapStateToProps = (state) => ({
reducer: state.reducer,
});
const mapDispatchToProps = {
onChangeAction: actions.someAction,
};
#connect(mapStateToProps, mapDispatchToProps)
class ChildComponent1 {
change = () => {
this.props.onChangeAction();//it dispatches to store
}
render() {
return (
<select onChange={this.change}>{render select}</select>
);
}
}

Is having a lot of render methods within a components bad practice?

I had written a component and there are some other instances of small components within that components. Is it a bad practice to use a renderxxx for each instance? thank you
It depends. If you are using some kind of logic for rendering your components, then you should go with dedicated renderX methods. But if all you are doing is to return the components, you should simply describe it with JSX.
Without logic
const MyComponent = ({ options }) => (
<div>
<SelectList options={options} />
</div>
);
With logic
const MyComponent = ({ options }) => {
const renderOptions = () => {
if (options.length < 5) {
return <RadioButtonGroup options={options} />;
}
return <SelectList options={options} />;
};
return (
<div>
{renderOptions()}
</div>
);
};
Another approch would be wrapping another component around every renderX method.
const OptionsRenderer = ({ options }) => {
if (options.length < 5) {
return <RadioButtonGroup options={options} />;
}
return <SelectList options={options} />;
};
const MyComponent = ({ options }) => (
<div>
<OptionsRenderer options={options} />
</div>
);

Resources