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" />
.....
)
}
Related
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
I am trying to get this componentFunction to re-render with the new data field on the state change that occurs changeValue and I don't know where I'm going wrong.
class Classname extends React.Component {
constructor() {
super();
this.state = {
value: "OriginalString",
};
}
changeValue = (newString) => {
this.setState({ value: newString });
this.forceUpdate();
};
componentFunction = () => {
return (
<div>
<component data={this.state.value} />
</div>
);
};
render() {
return (
<div>
<button
onClick={() => {
this.changeValue("updatedString");
}}
>
Update
</button>
<div className="control-section">
<DashboardLayoutComponent
id="dashboard_default"
columns={5}
cellSpacing={this.cellSpacing}
allowResizing={false}
resizeStop={this.onPanelResize.bind(this)}
>
<PanelsDirective>
<PanelDirective
sizeX={5}
sizeY={2}
row={0}
col={0}
content={this.componentFunction}
/>
</PanelsDirective>
</DashboardLayoutComponent>
</div>
</div>
);
}
}
ReactDOM.render(<Classname />, document.getElementById("root"));
<div id="root"></div>
<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>
Issue
The issue here is a stale enclosure of this.state.value in componentFunction.
Solution(s)
From what I can tell, the content prop of PanelDirective expects anything that returns, or resolves, to valid JSX (JSX attribute). A function callback to provide the content, a React component, or JSX literal all work.
Callback to reenclose updated state. Convert to a curried function that can enclose the current state when component is rendered. When attaching the callback you invoke the first function and pass the state value, the returned function is what PanelDirective will use when calling for the content value.
componentFunction = (data) => () => (
<div>
<component data={data} />
</div>
);
...
<PanelDirective
sizeX={5}
sizeY={2}
row={0}
col={0}
content={this.componentFunction(this.state.value)}
/>
React component. Convert componentFucntion to a React component and pass.
ComponentFunction = ({ data }) => (
<div>
<component data={data} />
</div>
);
...
<PanelDirective
sizeX={5}
sizeY={2}
row={0}
col={0}
content={<ComponentFunction data={this.state.value} />}
/>
JSX literal
<PanelDirective
sizeX={5}
sizeY={2}
row={0}
col={0}
content={
<div>
<component data={this.state.value} />
</div>
}
/>
Also, in case it wasn't obvious, you should remove the this.forceUpdate(); call in the changeValue handler. React state updates are sufficient in triggering the component to rerender.
try to pass in the value as param for the componentFunction, then each time the status value changed, the current component re-render, the trigger the function to re-render the child component using new state value.
class classname extends React.Component {
constructor() {
super();
this.state = {
value: "OriginalString",
};
}
changeValue = (newString) => {
this.setState({ value: newString });
this.forceUpdate();
}
componentFunction = (val) => {
return (
<div>
<component data={val} />
</div>
);
}
render() {
return (
<div>
<button onClick={() => { this.changeValue('updatedString') }}>Update</button>
<div className="control-section">
<DashboardLayoutComponent id="dashboard_default" columns={5} cellSpacing={this.cellSpacing} allowResizing={false} resizeStop={this.onPanelResize.bind(this)} >
<PanelsDirective>
<PanelDirective sizeX={5} sizeY={2} row={0} col={0} content={this.componentFunction(this.state.value)} />
</PanelsDirective>
</DashboardLayoutComponent>
</div>
</div>
);
}
}
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.
so I wanted to have a component iterate through an object within it's state and pass the data down to it's child. My parent component looks basically like this:
class ListContainer extends React.Component{
constructor(props){
super(props);
this.state = {"stuff": {
"pie": ["bread", "apple"],
"fries": ["potatoes", "oil"]
}
};
render(){
let rendArr = [];
for(recipe in this.state.stuff){
let newRecipe = <Child tableName={recipe} recipeData={this.state.stuff[recipe]} />;
rendArr.push(newRecipe);
}
return(
<div id="11"> I work
{rendArr}
</div>
);
}
However, I get an error saying that the "recipe" placeholder I used in the for loop isn't defined. I'm guessing I'm using the for loop here wrong with JSX, but I don't know the right way to iterate through an object. I know I could probably just convert it into an array of objects or something, but right now I'd like to understand why this for loop doesn't work in React.
In ReactJS: typical practice is to render lists using Array.prototype.map().
Object.entries() and Destructuring Assignment can be combined to reach a convenient form.
See below for a practical example.
// List.
class List extends React.Component {
// State.
state = {
recipes: {
pie: ['bread', 'apple'],
fries: ['potatoes', 'oil']
}
}
// Render.
render = () => (
<div id="11">
<h1>Map</h1>
{this.map()}
<h1>For Loop</h1>
{this.forLoop()}
</div>
)
// Map.
map = () => Object.entries(this.state.recipes).map(([name, recipe]) => <Recipe key={name} name={name} recipe={recipe}/>)
// For Loop.
forLoop = () => {
const list = []
for (let name in this.state.recipes) {
const recipe = this.state.recipes[name]
const line = <Recipe key={name} name={name} recipe={recipe}/>
list.push(line)
}
return list
}
}
// Recipe.
const Recipe = ({name, recipe}) => (
<div>
<h3 style={{textTransform: 'capitalize'}}>{name}</h3>
{recipe.map(ingredient => <div>{ingredient}</div>)}
</div>
)
ReactDOM.render(<List/>, document.querySelector('#root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Instead of pushing component in an array, push the object and pull that in the render using map method:
render(){
let rendArr = [];
for(recipe in this.state.stuff){
rendArr.push({tn: recipe, rd: this.state.stuff[recipe]});
}
return(
<div id="11"> I work
{
rendArr.map(el=> {
<Child tableName={el.tn} recipeData={el.rd} />
})
}
</div>
);
}
use map instead of for..in loop:
render(){
const rendArr = this.state.stuff.map((recipe, i) => <Child
key={i}
tableName={recipe}
recipeData={recipe}
/>);
return(
<div id="11"> I work
{rendArr}
</div>
);
}
how to pass an argument in props using functional component, here I had given my worked example code,
Let me explain, My click event will trigger from PopUpHandle when I click on the PopUpHandle I need to get the value from ContentSection component. ContentSection will be the listing, when clicking on each listing want to get the value of the current clicked list. I tried with this code my console printed undefined but I don't know how to handle with functional component.
class mainComponent extends Component {
constructor(props) {
super(props);
this.popTrigger = this.popTrigger.bind(this);
}
popTrigger(data){
console.log(data);
}
render(){
return(
<Popup popTrigger={this.popTrigger} />
)
}
}
export default mainComponent;
Popup component
const PopUpHandle = ({ popTrigger, value}) => <li onClick={popTrigger.bind(this, value)} />;
const ContentSection =({popTrigger, value}) =>(
<div>
{value === 'TEST1' && (
<div>
<PopUpHandle popTrigger={popTrigger} value={value} />
</div>
</div>
)}
{value === 'TEST2' && (
<div>
<PopUpHandle popTrigger={popTrigger} value={value} />
</div>
</div>
)}
</div>
)
const ContentList = (({ items, popTrigger}) => (
<div>
{items.map((value, index) => (
<ContentSection
key={`item-${index}`}
popTrigger={popTrigger}
index={index}
value={value}
/>
))}
</div>
)
);
class example extends Component {
constructor(props) {
super(props);
this.state = {
items: ['TEST1', 'TEST2', 'TEST3', 'TEST4'],
};
this.popTrigger = this.popTrigger.bind(this);
}
popTrigger(){
this.props.popTrigger()
}
render(){
return(
<ContentList popTrigger={this.popTrigger} items={this.state.items} />
)
}
}
export default example;
popTrigger(data){
console.log(data);
}
You didn't pass the data while calling this.props.popTrigger(). In javascript if you didn't pass the arguments, it will consider it as undefined.
The ContentSection component is not passed a value prop and hence its not passed on to the PopUpHandle component. Pass it like
render(){
return(
<ContentSection popTrigger={this.popTrigger} value={"test1"} />
)
}