I am trying to get some datas from child to parent. There is a way I usually do, and it totally works. But for one page I used:
<Link to={{
pathname: `/productBuy`,
state: { product, iconClick }
}}>
and when I send another prop from App.js to productBuy page, it's shown under product and it's undefined.
Codes from App.js :
const [productInformation, setProductInformation] = useState([]);
<Route path="/productBuy" render={props => <ProductBuy {...props} productInformation {productInformation} setProductInformation={setProductInformation} />} />
productBuy.js :
const ProductBuy = (productInfo, {productInformation,setProductInformation}) => {
return (
<div className="productBuy">
<div className="productBuyContainer">
<ProductLeft productInfo={productInfo.location.state} />
<ProductRight productInfo={productInfo.location.state} productInformation={productInformation} setProductInformation={setProductInformation}/>
</div>
</div>
);
}
When I console.log, my props are shown under product object as undefined. and when I invoke a function, an error appears: ProductRight.js:51 Uncaught TypeError: setProductInformation is not a function
Is there a way to solve this problem?
First of all you're missing the = after productInformation in the render prop:
<ProductBuy {...props} productInformation={productInformation} setProductInformation={setProductInformation} />
And the second issue is that you're unpacking the props incorrectly. Both productInformation and setProductInformation are available in the props argument (the first positional argument) in your function, but you're unpacking it in the second argument instead:
// INCORRECT
const ProductBuy = (productInfo, {productInformation,setProductInformation}) => { ... }
You can unpack it from the productInfo argument, which is an object that holds all the props:
const ProductBuy = (productInfo) => {
const { productInformation, setProductInformation } = productInfo;
return (
<div className="productBuy">
<div className="productBuyContainer">
<ProductLeft productInfo={productInfo.location.state} />
<ProductRight productInfo={productInfo.location.state} productInformation={productInformation} setProductInformation={setProductInformation}/>
</div>
</div>
);
}
You can also choose to unpack it at the top level:
const ProductBuy = ({ location, productInformation, setProductInformation }) => {
return (
<div className="productBuy">
<div className="productBuyContainer">
<ProductLeft productInfo={location.state} />
<ProductRight productInfo={location.state} productInformation={productInformation} setProductInformation={setProductInformation}/>
</div>
</div>
);
}
Add equal sign when passing the productInformation to props seems you forgot that in App.js
<Route path="/productBuy" render={props => <ProductBuy {...props} productInformation={productInformation} setProductInformation={setProductInformation} />} />
Related
I'm using the useState hook to manage rendering components on screen. I want to initialize it with a component while passing in the useState function to set the screen into the component.
Here is my App.js. The error I get is in regards to passing a function into itself on initialization.
function App() {
//useState hooks to determine which component should render
const [screenLoaded, loadScreen] = useState(() => {
<Home setLoadedScreen = {loadScreen}/>
})
return (
<div className="App">
{screenLoaded}
</div>
);
}
The default value for useState is always in the parentheses, no curly braces are needed in this case. const [state, setState] = useState(default). This state could be change in the future with setState(new value).
one simple method is to give your screen a name for example
<Home /> === "home-screen"
<About /> === "about-screen"
so when you pass the setState method of loadScreen into the component, you can switch them by setting the string, for example if you're in home screen and you want to switch to about screen, you'd write
setLoadedScreen("about-screen")
function App(){
const [screenLoaded, loadScreen] = useState("home-screen")
return (
<div className="App">
{screenLoaded === "home-screen" && <Home setLoadedScreen = "loadedScreen" />}
{screenLoaded === "about-screen" && <About setLoadedScreen = "loadedScreen" />}
</div>
);
}
I am new to React and I still try to understand how it all works.
Let's say I have got three components
The main App component where all other components are invoked looks like that:
function App() {
return (
<Router>
<div>
<Route path="/object-list" exact component={ObjectsList}/>
<Route path="/" exact component={Header}/>
</div>
</Router>
);
}
The Header component is that:
function Header() {
return (
<header>
<Navigation/>
<WebStart/>
</header>
)
}
where the WebStart is form I want to pass data from.
const WebStart = () => {
const [inputName, setInputName] = useState("")
const [inputCity, setInputCity] = useState("")
const [selectedObjects, setSelectedObjects] = useState([])
let objects = [
{name: "Object1", city: "City1"},
{name: "Object2", city: "City2"}
]
const handleSubmit = e => {
e.preventDefault()
const objectToAdd = {inputName, inputCity};
setSelectedObjects(objectToAdd)
}
return (
<div>
<div className="webstart-page">
<form onSubmit={handleSubmit}>
<input value={inputName}/>
<input value={inputCity}/>
<input type="submit"
value="search"
/>
</form>
</div>
)
}
What I want to achieve is save the state with data from submitting form and then send it to ObjectsList component
const ObjectsList = ({selectedObjects}) => {
return (
<div>
{
console.log(selectedObjects)
}
</div>
);
}
What I tried was the way you can see above. I also tried to
build Header component like this:
function Header() {
const [selectedObjects, setSelectedObjects] = useState([])
return (
<header>
<Navigation/>
<WebStart setSelectedObjects={setSelectedObjects}/>
<ObjectsList selectedObjects={selectedObjects}/>
</header>
)
}
Whatever I do I get undefined when try to console log selectedObjects in ObjectsList components and my ideas has just finished. I really do not know how do I need to pass this submitted data to other component.
You are trying to share the state between components which is rendered in different routes. We can achieve this in react via Context
Here is the working example Sharing States Between Routes
When trying to pass a component as a prop of another component, everything works fine.
But if i want instead pass a Component and handle its css classes inside the children, I'm currently lost.
In my mind im trying to achieve something similar to this:
import Navbar from 'what/ever/path/Navbar/is/in/Navbar.js';
export default function ParentComponent {
return(
<Navbar NavIcon={<MyIcon/>} />
)
}
.... Imports etc...
export default function Navbar(props) {
const {NavIcon} = props;
return(
<Navigation>
// Now use the Prop as a Component and pass default classNames to it.
// So that we don't need to wrap everything inside a span / div etc.
<NavIcon className="AddCustomStylesAlwaysHere" />
</Navigation>
)
}
Two approaches come to my mind:
Passing a component
Just pass the component and let the parent take care of its instantiation. This way, the only changes you need is making sure <MyIcon /> accepts a className prop:
const MyIcon = ({ className }) => {
return <div className={className} />
};
const Navbar = ({ NavIcon }) => {
return (
<Navigation>
<NavIcon className="AddCustomStylesAlwaysHere" />
</Navigation>
);
};
<Navbar NavIcon={MyIcon} />
Passing an element instance
This way, you take care of instantiating the component and the parent just renders it. In this case, you have to use React utilities to modify existing elements (https://reactjs.org/docs/react-api.html#cloneelement):
const MyIcon = ({ className }) => {
return <div className={className} />
};
const Navbar = ({ NavIcon }) => {
return (
<Navigation>
{React.cloneElement(NavIcon, { className: 'AddCustomStylesAlwaysHere' })}
</Navigation>
);
};
<Navbar NavIcon={<MyIcon />} />
You can use React.Children.map in combination with React.cloneElement:
{
React.Children.map(children, ( child, idx ) => {
return React.cloneElement(child, { className: 'additional-classnames' })
})
}
I am a beginner in React and what I am doing might not make sense.
What I am trying to do is just to pass a data to from a parent component which is used in every screen to children.
My code is like this.
AppDrawer.tsx
const AppDrawer: React: FC<Props> = ({
children,
}) => {
const [aString, setString] = React.useState<string>('Hello');
...
<div>
<Drawer>
...
</Drawer/>
<main>
<div>
<Container>
{children}
</Container>
</div>
</main>
</div>
App.tsx
<Swith>
<AppDrawer>
<Route path="/" component={ChildCompoent} />
...
...
</AppDrawer>
</Switch>
ChildComponent.tsx
export default class ChildComponent extends React.Component<Props, State> {
state = {
..
}
}
And now I want to access aString in AppDrawer.tsx in child components but I couldn't figure out how I can do it. How can I do it?
I think you can use Context here. Context allows you to pass down something from the parent component, and you can get it at the child component (no matter how deep it is) if you'd like.
I made an example link
You can read more about it here
Updated: I notice you use Route, Router, and I don't use in my codesandbox. However, it's fine though. The main idea is to use context :D
Use render in component Route
<Route
path='/'
render={(props) => (
<ChildCompoent {...props}/>
)}
/>
And should not props aString in component AppDrawer
I'm not sure about the version of react and if it is still supported but this how I did this in my app.
try checking for React. Children over here: https://reactjs.org/docs/react-api.html#reactchildren
see if it's suitable for your case.
render() {
const children = React.Children.map(this.props.children, child => {
return React.cloneElement(child, {
...child.props,
setHasChangesBeenMade: (nextValue) => this.setState({ hasChangesBeenMade: nextValue })
});
});
return (children[0]);
}
for you it should be something like this :
const AppDrawer: React: FC<Props> = ({
children,
}) => {
const [aString, setString] = React.useState<string>('Hello');
...
const childrens = React.Children.map(children, child => {
return React.cloneElement(child, {
...child.props,
aString: aString
});
});
<div>
<Drawer>
...
</Drawer/>
<main>
<div>
<Container>
{childrens[0]}
</Container>
</div>
</main>
</div>
I have a state variable dataSource that has some data in it.
In a parent component I have the following:
updateFeed = newItem => {
this.setState({ dataSource: this.state.dataSource.data.unshift(newItem) })
console.log(this.state.dataSource.data)
}
render() {
console.log(this.state.dataSource.data)
return (
<React.Fragment>
<Header />
<Route
path="/"
exact
component={props => (
<Feed {...props} feedData={this.state.dataSource.data} updateFeed={this.updateFeed} />
)}
/>
<Route path="/profile/:id" exact component={props => <Profile {...props} />} />
</React.Fragment>
)
}
updateFeed is called from the child component.
onSubmit = () => {
const newPost = newData // some new data
this.props.updateFeed(newPost)
}
The updateFeed function is getting executed on submit, and the console.log is giving the updated data. But inside the render function this.state.dataSource.data is undefined. What am I missing here?
You do dataSource: dataSource.data in your setState call, therefore dataSource.data in your render method will actually access dataSource.data.data which is probably undefined. May change updatedFeed to:
updateFeed = newItem => {
this.setState(prev => ({
dataSource: {
...prev.dataSource,
data: prev.dataSource.data.concat(newItem)
}
}));
}
Which ensures a pure state.
It is because previously, this.state.dataSource is an object having key data. So even you are setting new value in updateFeed but in the very next line, state has not been updated yet. React does this asynchronously. so your log statement is showing old data.
You need to update state like this
const dataSource = this.state.dataSource;
dataSource.data.unshift(newItem);
this.setState({ dataSource: dataSource })