I'm having a trouble with passing props to children in functional react. My {children} is a Details component like below:
<SidebarPage>
{children}
</SidebarPage>
const Details = () => {}
how can I pass props in way that, I put props to {children} and I receive this props in Details component? Is this even workable?
Thanks for any help!
You can pass component as prop, something like -
const SidebarPage = ({ChildComponent}) => {
const props = { name: "X" };
return <ChildComponent {...props} />
}
const Details = (props) => {}
<SidebarPage ChildComponent={Details} />
You could use cloneElement to add new props to a component children
const newProps = {aProp: "aProp"} ;
return (
<SidebarPage>
{React.Children.map(props.children, child => {
return React.cloneElement(child, {
newProps,
}, null)
}
</SidebarPage>
)
Related
I have the following code `
import { useState, useEffect } from 'react'
import LayoutContent from './layout_content';
type Props = {
children: JSX.Element | JSX.Element[]
}
const Layout = ({ children }: Props) => {
const [selected, setSelected] = useState(countries[0]);
const country= selected.id
return (
<>
<Sidebar onClick={toggle} sidebar={open} />
<LayoutContent sidebar={open} countriesWithsrc ={countriesWithsrc} selected={selected} lected={setSelected} >
{children}
</LayoutContent>
</>
)
}
export default Layout;
`
How do I pass the variable country from the Layout component to the children without state management ?.I.e I want to drill it.
If you don't want any state management you can use React.Children. It provides utilities to work with the children prop. React.Children.map will run a method for every immediate child of the component. You can use cloneElement along with that to create a clone of your element by passing in extra properties. Infact you can even modify the children of an element you are cloning, but that is not the ask here.
Do note that context would be the better way to do it.
const Layout = ({ children }: Props) => {
....
....
const modifiedChildren = React.Children.map(children, child => {
if (React.isValidElement(child)) {
return React.cloneElement(child, { testProp : 'test' });
}
return child;
});
....
....
return (
<>
<Sidebar onClick={toggle} sidebar={open} />
<LayoutContent sidebar={open} countriesWithsrc ={countriesWithsrc} selected={selected} lected={setSelected} >
{modifiedChildren}
</LayoutContent>
</>
)
}
I have 2 components that pass to the child the prop data, but only one parent pass the prop numPage and the prop setNumPage,
When I try to use the optional sign in the interface it tells me that React.Dispatch<React.SetStateAction> cannot be undefined. I found a solution that is inadequate, use any, can you give me another solution?
First parent
const Home = () => {
const [searchParams] = useSearchParams()
const [numPages, setNumPages] = useState<number>(1)
const url:string = searchParams.get("search") ? `${SEARCH_URL}${searchParams.get("search")}${PAGES}${numPages}` : `${POPULAR_RESULTS}${numPages}`;
const {data, loading} = useFetch<movieApi>(url);
if(loading) return <Sppiner/>
return (
<div>
<Items
data={data}
numPages={numPages}
setNumPages={setNumPages}
/>
</div>
);
};
Second parent
const GenresPage = () => {
const { data, loading } = useFetch<movieApi>(POPULAR_RESULTS);
if(loading) return <Sppiner/>
return (
<div>
<Items data={data} />
</div>
);
};
export default GenresPage;
Child
interface DataProps {
data: movieApi | null;
numPages:number;
setNumPages:React.Dispatch<React.SetStateAction<number>>;
}
const Items = ({ data,numPages,setNumPages }:DataProps) => {}
To the child if I put any or DataProps it works, but I don't wanna do that.
You can change setNumPages inside DataProps to:
setNumPages: (value: number) => void
I am trying to use props inside function of functional props.
const Inputs = (props) => {
console.log(props.printFirstArray);
const FirstInputSet = () => {
console.log(props.printFirstArray)
}
}
First console.log is logging the value of printFirstArray, but second console.log inside FirstInputSet() function is not logging anything.
Edit: Minimal Code
const Inputs = (props) => {
const FirstInputSet = () => {
return (
<>
<div className="first input-set">
{props.printFirstArray}
</div>
</>
);
}
const renderFirstInputSet = () => {
if (props.firstInputValue)
return <FirstInputSet />
else
return null;
}
return (
<>
{renderFirstInputSet()}
</>
);
}
Neither props.printFirstArray nor props.printSecondArray is not returning anything
From your code, FirstInputSet is a new component and you aren't passing any props.
return <FirstInputSet />
Try to pass your props to the child component but I recommend keeping the child component out of parent component.
OR
If you meant to use FirstInputSet as a function, you can modify the code like below
return FirstInputSet();
I have two React components, Parent and Child. Both must be function components. I am trying to change the state of Child from Parent. I believe the best way to do this is using refs, but I haven't been able to get it to work.
I've tried creating a ref in Parent and passing it down to child, but this causes an error. I considered forwardRef() but I'm not sure that will work either.
const Parent = () => {
const ref = React.useRef();
const closeChild = () => {
ref.current.setOpen(false);
};
return (
<div>
<Child ref={ref} onClick={closeChild} />
</div>
);
};
const Child = () => {
const [open, setOpen] = useState(false);
return (
<div>
{open ? <p>open</p> : <p>closed</p>}
</div>
);
};
The code as it is now produces this error message:
react-dom.development.js:506 Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?`
Only stateful React components can expose the ref automatically. If using functional component, I think you'll need to use forwardRef for the child component:
e.g.
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
From the docs,
Refs provide a way to access DOM nodes or React elements created in the render method.
refs are not meant for changing the state.
You actually need useState in parent component, and later you can manage it from child component.
const Parent = () => {
const [open, setOpen] = useState(true);
const toggleChild = () => {
setOpen(!open)
};
return (
<div>
<Child onClick={toggleChild} open={open}/>
</div>
);
};
const Child = (props) => {
return (
<div>
{props.open ? <p onClick={props.onClick}>open</p> : <p onClick={props.onClick}>closed</p>}
</div>
);
}
Demo
I don't think you need refs here, you should really try to avoid them.
avoid using refs for anything that can be done declaratively. They are useful for:
1.Managing focus, text selection, or media playback.
2.Triggering imperative animations.
3.Integrating with third-party DOM libraries.
https://reactjs.org/docs/refs-and-the-dom.html
why not just send the value into the child through props?
const Parent = () => {
const [open] = useState(false);
const toggleChild = () => {
this.setState(prevState => {open: !prevState.open});
};
return (
<div>
<Child onClick={toggleChild} open={this.state.open}/>
</div>
);
};
const Child = (props) => {
return (
<div>
{props.open ? <p>open</p> : <p>closed</p>}
</div>
);
};
EDIT: forgot you were using hooks. This is the way then
const [open, setOpen] = useState(false);
const toggleChild = () => {
setOpen(!open);
};
return (
<div>
<Child onClick={toggleChild} open={open}/>
</div>
);
EDIT 2: #ravibagul91 pointed out that you need to put the onClicks in the children <p> elements as well, look at his answer
class List extends Component {
<DoSomethingToList>
{this.props.list.map(item => <Item key={item} ref={item} />}
</DoSomethingToList>
}
class DoSomethingToList extends Component {
componentDidUpdate(prevProps) {
// I want to access refs here
let refs = this.refs
// refs === {} ------> Why is this happening?
}
render() {
<div>
{this.props.children}
</div>
}
}
I want to be able to access children as refs in the lifecycle methods of the wrapper component. This is so I can keep track of previous instances of domNodes for animations. But whenever I access refs, they return as an empty object. What is the React-friendly way of accessing the refs in lifecycle methods?
The parent/container component (DoSomethingToList) should keep a track of refs.
const List = ({list}) => (
<DoSomethingToList>
{ctx => (
list.map((item) => <Item key={item} ref={ctx.refs[item]} />)
)}
</DoSomethingToList>
)
class DoSomethingToList extends Component {
state = {refs: []};
componentDidUpdate(prevProps, prevState) {
let refs = prevState.refs;
// do something with refs...
}
render() {
<div>
{this.props.children(this.state)}
</div>
}
}
I honestly is a bad practice as React would not know if any of state of element held by each ref change.
So I'd recommend exposing a callback that child component can call to let parent know of the change back to the parent.
const List = ({list}) => (
<DoSomethingToList>
{ctx => (
list.map((item) =>
<Item key={item} value={ctx.values[item]} onUpdate={ctx.updateValue} />)
)}
</DoSomethingToList>
)
class DoSomethingToList extends Component {
state = {
values: [],
updateValue: this.updateValue
};
updateValue = (value, i) => {
const {values} = prevState;
const newValues = {...values};
newValues[i] = value;
this.setState({values: newValues});
}
componentDidUpdate(prevProps, prevState) {
const {values} = prevState;
// do something with values...
}
render() {
<div>
{this.props.children(this.state)}
</div>
}
}