I am using the common use case indicated in the ReadMe:
const Desktop = props => (
<Responsive {...props} minWidth={1680} maxWidth={2560} />
)
const LaptopL = props => (
<Responsive {...props} minWidth={1440} maxWidth={1679} />
)
...
and I have a prop (bottleState) that I am trying to pass to specific components inside the Responsive component (eg Desktop):
const WineHeader = ({ bottleState }) => (
<HeaderCard>
<Desktop>
<WineBox />
<WineBackgroundBox />
<VineyardBackgroundBox />
<WineInfoContainer>
<LeftContainer>
<WineTypeBox bottleState={bottleState} />
<WineTitleBox bottleState={bottleState} />
<WineDescriptionBox bottleState={bottleState} />
</LeftContainer>
<WineProperties />
</WineInfoContainer>
</Desktop>
<LaptopL>
<WineBox />
<WineBackgroundBox />
<VineyardBackgroundBox />
<WineInfoContainer>
<LeftContainer>
<WineTypeBox />
<WineTitleBox />
<WineDescriptionBox />
</LeftContainer>
<WineProperties />
</WineInfoContainer>
</LaptopL>
...
</HeaderCard>
)
WineHeader.propTypes = Object.freeze({
bottleState: PropTypes.object, //eslint-disable-line
})
export default WineHeader
When logging the bottleState prop in one of the above child components in which I am trying to access it - it is not available (the log returns undefined) :
const WineTypeBox = ({ bottleState }) => (
<WineTypeStyle>{console.log(bottleState)}</WineTypeStyle>
)
> undefined ---- WineTypeBox.jsx?a13c:36
and when I simply remove the Responsive component, I can access the bottleState prop as expected:
const WineHeader = ({ bottleState }) => (
<HeaderCard>
<WineBox />
<WineBackgroundBox />
<VineyardBackgroundBox />
<WineInfoContainer>
<LeftContainer>
<WineTypeBox bottleState={bottleState} />
<WineTitleBox bottleState={bottleState} />
<WineDescriptionBox bottleState={bottleState} />
</LeftContainer>
<WineProperties />
</WineInfoContainer>
...
</HeaderCard>
)
returns the bottleState object when logged to the console:
{wineCollection: "Classics", wineType: "Semi Sweet Red", bottleName: "Khvanchkara", bottleImage: Array(1), bottleNoteText: "<p>test</p>", …}
bottleImage: ["http://localhost/uploads/bottle.png"]
bottleName: "Khvanchkara"
bottleNoteText: "<p>test</p>"
temperatureHigh: null
vintage: null
volume: null
wineCollection: "Classics"
wineType: "Semi Sweet Red"
__proto__: Object ---- WineTypeBox.jsx?a13c:36
Any ideas why this is the case? I have tried defining the desktop function inside the WineHeader functional component, because that is the function where I am pulling off the bottleState prop from this.props but this doesn't change the behaviour; when throwing a debugger before the return statement of the Desktop component, I can clearly see the bottleState prop being passed in, I do not even need it to be passed in as I am directly passing it into other components nested further down the DOM tree without any issue when the Desktop Component is not wrapping them, but the fact that my other components that need to access this prop are nested inside the Desktop component is causing the props to be blocked for some reason. Any help is greatly appreciated. Thanks,
Corey
So, it's possible that you're expecting to look at the <Desktop> and actually looking at the <LaptopL> screen:
const WineHeader = ({ bottleState }) => (
<HeaderCard>
<Desktop>
...
<WineTypeBox bottleState={bottleState} />
<WineTitleBox bottleState={bottleState} />
<WineDescriptionBox bottleState={bottleState} />
...
</Desktop>
<LaptopL>
...
<WineTypeBox /> // <- pass props here
<WineTitleBox /> // <- pass props here
<WineDescriptionBox /> // <- pass props here
...
</LaptopL>
</HeaderCard>
)
Which you mentioned fixed it in the comments above. Kudos!
For reference:
It's also good practice with react-responsive to add a <Default ...> wrapper.
This can happen with other libraries, and require a different fix.
Eg. The initial destructure of props into ({ bottleState }) might mean that react-responsive can't access props in order to pass them onto their child components. When this happens with external libraries, it can be fixed in several ways:
Pass the props with the spread syntax
const WineHeader = props => (
...
...
...
)
OR
const WineHeader = ({bottleState, foo, bar}) => (
...
<Desktop>
...
<WineTypeBox {...{bottleState, foo, bar}} />
...
</Desktop>
)
Pass a single component, or wrap the libraries JSX inner components because the library can't handle multiple child components
const WineHeader = ({bottleState}) => (
...
OR
const WineHeader = ({bottleState}) => (
<HeaderCard>
<Desktop>
<Fragment>
<WineTitleBox bottleState={bottleState} />
<WineTitleBox bottleState={bottleState} />
...
</Fragment>
</Desktop>
Passing props using React Context
Related
I would like to change a true/false state in a child component and pass it to a parent component, where the state is actually defined. Right now, this results only in an error.
Parent:
const PostTemplate = ({ data }) => {
const [isSlider, setIsSlider] = useState(false);
return (
<Layout class="page">
<main>
<Views setIsSlider={isSlider} {...data} />
</main>
</Layout>
)}
</>
);
};
Child:
const Views = (data) => {
return (
<div
key="views"
className="post__gallery views"
>
{data.views.edges.map(({ node: view }) => (
<divy
onClick={() => setIsSlider(true)}
>
<GatsbyImage
image={view.localFile.childImageSharp.gatsbyImageData}
/>
<div
className="post__caption"
dangerouslySetInnerHTML={{
__html: view.caption,
}}
/>
</div>
))}
</div>
);
};
Right now, this puts out:
setIsSlider is not a function.
(In 'setIsSlider(true)', 'setIsSlider' is false)
Perhaps also relevant the console.log from React:
Can't perform a React state update on an unmounted component.
This is a no-op, but it indicates a memory leak in your application.
To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
You're passing isSlider as a prop. You should be passing setIsSlider.
Note that state management in React is a complex topic and you probably want to do some research into how it is done in general. Directly passing a state setting callback works, but it doesn't scale well to complex applications.
Instead of passing the state variable you have to pass the state function like this:
Parent
const PostTemplate = ({ data }) => {
const [isSlider, setIsSlider] = useState(false);
return (
<Layout class="page">
<main>
<Views setIsSlider={setIsSlider} {...data} />
</main>
</Layout>
)}
</>
);
};
Child
You've to use data.<function_passed_via_props> to access the setIsSlider function, like this:
const Views = (data) => {
return (
<div
key="views"
className="post__gallery views"
>
{data.views.edges.map(({ node: view }) => (
<divy
onClick={() => data.setIsSlider(true)}
>
<GatsbyImage
image={view.localFile.childImageSharp.gatsbyImageData}
/>
<div
className="post__caption"
dangerouslySetInnerHTML={{
__html: view.caption,
}}
/>
</div>
))}
</div>
);
};
To Fix: Change setIsSlider={isSlider} into setIsSlider={setIsSlider}.
However, in case you need to manage more than a few states across components. I would suggest using Redux. This can centralize your common-used states, you can access or update these states in any component using Redux.
I'm not sure why I can't use my pass on useState from Parent component <AppMain /> to the child component <GroupPage /> and <CreateGroupForm />
What I'm trying to do:
I'm working on an update functionality, where on clicking the edit button in <GroupPage />, I want the content of GroupPage to fill on the form fields of <CreateGroupForm />. So for that, I have created states currentId and setCurrentId in <AppMain /> since it's the parent component of both, and I can pass on these to its child components assuming they both share the states.
const AppMain = () => {
const [ currentId, setCurrentId ] = useState(null)
return (
<div>
<Switch>
<Route path="/groupMain" exact> <GroupMain /> </Route>
<Route path="/groupMain/:id" exact> <GroupPage setCurrentId={setCurrentId} /> </Route>
<Route path="/createGroup" exact> <CreateGroupForm currentId={currentId} setCurrentId={setCurrentId} /> </Route>
</Switch>
</div>
)
}
export default AppMain
const GroupPage = ({setCurrentId}) => {
const { group } = useSelector((state) => state.groups)
// the reason for this condition is to prevent rendering something before data is actually fetched
if(!group) return null
return (
<div>
<EditButton onClick= {() => {
setCurrentId(group._id)
history.push(`/createGroup/`)
}} />
<h1>{group.groupName}</h1>
</div>
)
}
export default GroupPage
Now when clicking on the edit button of <GroupPage /> I'm setting the current group Id in setCurrentId and directing it to the <CreateGroupForm />. In <CreateGroupForm /> I'm checking if currentId matches the one with the already existed group. And by useEffect I'm populating those values in form fields.
const CreateGroupForm = ({currentId, setCurrentId}) => {
const [groupData, setGroupData] = useState({
groupName: ''
})
const group = useSelector((state) => currentId ? state.groups.groups.find((grp) => grp._id === currentId) : null)
console.log(group) // null
console.log(currentId) // undefined
useEffect(() => {
if(group) setGroupData(group)
}, [group])
return (
<div>
<MainForm>
<form autoComplete="off" onSubmit={handleSubmit}>
<h1>{ currentId ? 'Editing' : 'Creating'} a Group:</h1>
<label htmlFor="Group Name">Your Group Namee: </label>
<input type="text" value={groupData.groupName} onChange={(e) => setGroupData({ ...groupData, groupName: e.target.value })} />
<button type="submit">Submit</button>
</form>
</MainForm>
</div>
)
}
export default CreateGroupForm
What is happening:
On clicking the Edit button, the form fields are not populating with the group content.
Please any help would be appreciated.
It's not a good practice to pass setStates between its children. I recommend you to create callback functions in your appMain, and pass that functions to your GroupPage and CreateGroupForm components. When you call those functions inside the components your functions will change your currentIdState in the appMain. Changing the state of currentId in your appMain the components will recive the new state of currentId
My props on an HOC do not seem to be overriding. I feel as though it might be the notation I'm using. Here is what I have right now.
export const HOCComponent = ({ someProp }) => (
<ContainerComponent
propOne={someValueOne}
propTwo={someValueTwo}
propThree={someValueThree)
/>
);
export const wrapperComponent = props =>(
<HOCComponent {...props} propThree={someValueFour}/>
);
someValueFour does not seem to override someValueThree. Any suggestions would be super helpful! Thank you.
Swap the order of the passed props such that anything you pass later overrides anything passed previously.
export const wrapperComponent = props =>(
<HOCComponent propThree={someValueFour} {...props} />
);
The HOCComponent wrapper component needs to also pass along all props to the component it's wrapping.
export const HOCComponent = (props) => (
<ContainerComponent
propOne={someValueOne}
propTwo={someValueTwo}
propThree={someValueThree}
{...props}
/>
);
Just a minor point about terminology, nothing in your code snippet is a Higher Order Component. HOCs consume a React component and return a new React Component.
An Example:
const withMyHOC = WrappedComponent => props => {
// any HOC logic
return (
<Wrapper
// <-- any wrapper props
>
<WrappedComponent
{...props} // <-- pass props through
// <-- override any props
/>
</Wrapper>
);
};
usage:
export default withMyHOC(SomeComponent);
I saw you don't pass props from HOCComponent to ContainerComponent so propThree is not override. You need to pass someProp to ContainerComponent:
<ContainerComponent propOne propTwo propThree {...someProp} />
I know you can pass all a react components props to it's child component like this:
const ParentComponent = () => (
<div>
<h1>Parent Component</h1>
<ChildComponent {...this.props} />
</div>
)
But how do you then retrieve those props if the child component is stateless? I know if it is a class component you can just access them as this.prop.whatever, but what do you pass as the argument into the stateless component?
const ChildComponent = ({ *what goes here?* }) => (
<div>
<h1>Child Component</h1>
</div>
)
When you write
const ChildComponent = ({ someProp }) => (
<div>
<h1>Child Component {someProp}</h1>
</div>
)
From all the props that you are passing to the childComponent you are just destructuring to get only someProp. If the number of props that you want to use in ChildComponents are countable(few) amongst the total number of props that are available, destructuring is a good option as it provides better readability.
Suppose you want to access all the props in the child component then you need not use {} around the argument and then you can use it like props.someProp
const ChildComponent = (props) => (
<div>
<h1>Child Component {props.someProp}</h1>
</div>
)
Are you looking for the ES6 named argument syntax (which is merely destructuring) ?
const ChildComponent = ({ propName }) => (
<div>
<h1>Child Component</h1>
</div>
)
const ChildComponent = (props) => ( // without named arguments
<div>
<h1>Child Component</h1>
</div>
)
Optionally there is a second argument to your function depending of whether you specified a context for your component or not.
Perhaps it would be more helpful wityh a links to the docs. As stated in the first article about functional components. Whatever props passed on to the component is represented as an object passed as first argument to your functional component.
To go a little further, about the spread notation within jsx.
When you write in a component :
<Child prop1={value1} prop2={value2} />
What your component will receive is an plain object which looks like this :
{ prop1: value1, prop2: value2 }
(Note that it's not a Map, but an object with only strings as keys).
So when you're using the spread syntax with a JS object it is effectively a shortcut to this
const object = { key1: value1, key2: value2 }
<Component {...object}/>
Is equivalent to
<Component key1={value1} key2={value2} />
And actually compiles to
return React.createElement(Component, object); // second arg is props
And you can of course have the second syntax, but be careful of the order. The more specific syntax (prop=value) must come last : the more specific instruction comes last.
If you do :
<Component key={value} {...props} />
It compiles to
React.createElement(Component, _extends({ key: value }, props));
If you do (what you probably should)
<Component {...props} key={value} />
It compiles to
React.createElement(Component, _extends(props, { key: value }));
Where extends is *Object.assign (or a polyfill if not present).
To go further I would really recommend taking some time to observe the output of Babel with their online editor. This is very interesting to understand how jsx works, and more generally how you can implement es6 syntax with ES5 tools.
const ParentComponent = (props) => (
<div>
<h1>Parent Component</h1>
<ChildComponent {...props} />
</div>
);
const ChildComponent = ({prop1, ...rest}) =>{
<div>
<h1>Child Component with prop1={prop1}</h1>
<GrandChildComponent {...rest} />
</div>
}
const GrandChildComponent = ({prop2, prop3})=> {
<div>
<h1>Grand Child Component with prop2={prop1} and prop3={prop3}</h1>
</div>
}
You can use Spread Attributes reducing code bloat. This comes in the form of {'somearg':123, ...props} or {...this.props}, with the former allowing you to set some fields, while the latter is a complete copy. Here's an example with ParentClass.js :
import React from 'react';
import SomeComponent from '../components/SomeComponent.js';
export default class ParentClass extends React.Component {
render() {
<SomeComponent
{...this.props}
/>
}
}
If I do, <ParentClass getCallBackFunc={() => this.getCallBackFunc()} />, or if I do <ParentClass date={todaysdatevar} />, the props getCallBackFunc or date will be available to the SomeComponent class. This saves me an incredible amount of typing and/or copying/pasting.
Source: ReactJS.org: JSX In Depth, Specifying the React Element Type, Spread Attributes. Official POD:
If you already have props as an object, and you want to pass it in JSX, you can use ... as a “spread” operator to pass the whole props object. These two components are equivalent:
return <Greeting firstName="Ben" lastName="Hector" />;
}
function App2() {
const props = {firstName: 'Ben', lastName: 'Hector'};
return <Greeting {...props} />;
}```
Now, let's apply this to your code sample!
const ParentComponent = (props) => (
<div>
<h1>Parent Component</h1>
<ChildComponent {...props} />
</div>
);
I thought I would add a simple ES2015, destructuring syntax I use to pass all props from a functional parent to a functional child component.
const ParentComponent = (props) => (
<div>
<ChildComponent {...props}/>
</div>
);
Or if I have multiple objects (props of parent, plus anything else), I want passed to the child as props:
const ParentComponent = ({...props, ...objectToBeAddedToChildAsProps}) => (
<div>
<ChildComponent {...props}/>
</div>
);
This destructuring syntax is similar to the above answers, but it is how I pass props along from functional components, and I think it is really clean. I hope it helps!
But how do you then retrieve those props if the child component is stateless?
const ChildComponent = ({ *what goes here?* }) => (
<div>
<h1>Child Component</h1>
</div>
)
ChildComponent holds the name and the props will be the argument in the arrow function syntax just as you need:
const ChildComponent = props => (
<div>
<p>{props.value ? props.value : "No value."}</p>
</div>
);
If you Babel-it it will create something like this:
var ChildComponent = function ChildComponent(props) {
return React.createElement(
"div",
null,
React.createElement(
"p",
null,
props.value ? props.value : "No value."
)
);
};
For some reason, what seems to work for me is a variation on Shubham's answer above:
const ChildComponent = props => (
<div>
<h1>Child Component {props[0].someProp}</h1>
</div>
)
Using this
const ParentComponent = ({ prop1, prop2, prop3 }) => (
<div>
<h1>Parent Component</h1>
<ChildComponent {...{ prop1, prop2, prop3 }} />
</div>
);
const ChildComponent = ({ prop1, prop2, prop3 }) =>{
<div>
<h1>Child Component with prop1={prop1}</h1>
<h1>Child Component with prop2={prop2}</h1>
<h1>Child Component with prop2={prop3}</h1>
</div>
}
I'm a bit new to React, and have been practicing by creating an application using the enums rendering method specified in this article.
However, I'm trying to apply it in a slightly different way than the article talks about, more specifically using it to conditionally render all of my website except for the <Nav /> based on the lastLinkClicked state. I've got different page classes for each condition as listed in the WEB_PAGES object.
Maybe I'm misunderstanding this method, since I don't have much experience with enums, but my pages aren't rendering correctly. Here's my code:
class App extends Component {
constructor(props){
super(props);
this.state = {
x: ...
y: ...
z: ...
lastClickedLink: 'home' //changes to 'new', 'settings', etc. using another function not listed here
}
}
render() {
function onLinkClick(link) {
const WEB_PAGES = {
home: <Home
x={this.state.x}
/>,
new: <NewPost />,
settings: <Settings />,
signup: <SignUp />,
login: <Login />
};
return (
<div>
{WEB_PAGES.link}
</div>
);
}
return (
<div>
<Nav
y={this.state.y}
z={this.state.z}
/>
{onLinkClick(this.state.lastClickedLink)}
</div>
);
}
}
export default App;
I removed some code for brevity's sake. The error I'm getting with this setup is TypeError: Cannot read property 'state' of undefined for home under the WEB_PAGES object.
I initially thought that this was pointing to the WEB_PAGES object, but changing this to App showed that state was undefined as well. I'm not really sure what to do at this point.
Is the enums conditional rendering method even doable on this scale? And if not, what other method would be the most ideal for this situation? Many thanks!
In javascript, When you create a function using function keyword it creates his own new scope and also creates default object this. So while you were trying to access this.state.x then it will not state property inside the function. It becomes this.undefined.x. so it is giving the error.
Whereas arrow function {(() => {})} does not create this object but create internal scope.
try following render method in your code:
render() {
return (
<div>
<Nav
y={this.state.y}
z={this.state.z}
/>
{((link) => {
const WEB_PAGES = {
home: <Home
x={this.state.x}
/>,
new: <NewPost />,
settings: <Settings />,
signup: <SignUp />,
login: <Login />
};
return (
<div>
{WEB_PAGES[link]}
</div>
);
})(this.state.lastClickedLink)}
</div>
);
}
use {WEB_PAGES[link]} when you try to use . it will not work
const Link = ({ lastClickedLink }) => {
const WEB_PAGES = {
home: <Home x={lastClickedLink} />,
new: <NewPost />,
settings: <Settings />,
signup: <SignUp />,
login: <Login />
};
return (
<div>
{WEB_PAGES[link]}
</div>
);
}
render() {
return (
<div>
<Nav
y={this.state.y}
z={this.state.z}
/>
<Link lastClickedLink={lastClickedLink} />
</div>
);
}
This variant has more readability and extensibility. Based on Shubham Batra example.