I have a link in a nav-bar that takes me to an anchor on the index page. Currently I don't know how to put an id onto the component, so I have to wrap the component in a div and give it an id for it to work. Ideally, I would like to simply put the anchor on the component itself.
This works fine for me, but I'm wondering if this is the way to do an anchor with React/Gatsby or is there a better way?
//Navbar, which is part of Layout
export default class NavBar extends Component {
render() {
return (
<NavContainer>
<Menu>
<ul>
<li>Home</li>
<li>About</li>
<li>Events</li>
<li>Blog</li>
<li>Mentorship</li>
<li>
<Link to="/#join-us">Join Us</Link>
</li>
</ul>
</Menu>
</NavContainer>
)
}
}
//Homepage
const IndexPage = ({ data, location }) => {
const { site, events, about, features, blogs } = data
const eventsEdges = events.edges
return (
<Layout>
<div id="join-us">
<JoinUs /> //Can't do <JoinUs id="join-us"/>
</div>
<BlogList blogs={blogs} fromIndex={true} />
</Layout>
)
}
You have to pass id as a props to your JoinUs component.
First of all, do <JoinUs id="join-us" />. Now, id is a props of your component.
JoinUs component
const JoinUs = ({ id }) => (
<div id={id}>
...Your component stuff
</div>
);
Other method
import React from 'react'
class JoinUs extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div id={this.props.id}>
... Your component stuff
</div>
);
}
}
export default JoinUs
The two methods are similar but the first one is more concise.
The line JoinUs = ({ id }) ... allows you to access and destructure props. You get property id from your props. Now, you don't have to wrap your component in a div with an anchor
More information here : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
Related
Using ReactJS, I am trying to create a common workspace component that will have toolbar buttons and a navigation menu. The idea I have is to re-use this component to wrap all other dynamic components that I render in the app.
Currently, I've created a Toolbar and MenuBar components that I then add to each component in the app as such:
<Toolbar/>
<MenuBar/>
<Vendors/>
This does not feel right, since my aim is to have just one component which would be something like:
<Workspace>
<Vendor/>
</Workspace>
However, I am not sure of how to achieve this and whether this is the right approach.
As to whether or not it is the right approach is subjective, but I can provide insight into one way to make a "wrapper" type component:
// Your workspace wrapper component
class Workspace {
render() {
return (
<div className="workspace">
<div className="workspace__toolbar">
Toolbar goes here
</div>
<div className="workspace__nav">
Navgoes here
</div>
<div className="workspace__content">
{this.props.children}
</div>
</div>
)
}
}
// Using the component to define another one
class MyComponent {
render() {
return (
<Workspace>
This is my workspace content.
</Workspace>
)
}
}
You can also look at HOC's or Higher Order Components to wrap things.
React offer two traditional ways to make your component re useable
1- High-order Components
you can separate the logic in withWorkspace and then give it a component to apply that logic into it.
function withWorkSpace(WrappedComponent, selectData) {
// ...and returns another component...
return class extends React.Component {
render() {
// ... and renders the wrapped component with the fresh data!
// Notice that we pass through any additional props
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
}
const Component = () => {
const Content = withWorkSpace(<SomeOtherComponent />)
return <Content />
}
2- Render Props
or you can use function props then give the parent state as arguments, just in case you need the parent state in child component.
const Workspace = () => {
state = {}
render() {
return (
<div className="workspace">
<div className="workspace__toolbar">
{this.props.renderTollbar(this.state)}
</div>
<div className="workspace__nav">
{this.props.renderNavigation(this.state)}
</div>
<div className="workspace__content">
{this.props.children(this.state)}
</div>
</div>
)
}
}
const Toolbar = (props) => {
return <div>Toolbar</div>
}
const Navigation = (props) => {
return <div>Toolbar</div>
}
class Component = () => {
return (
<Workspace
renderNavigation={(WorkspaceState) => <Navigation WorkspaceState={WorkspaceState} />}
renderTollbar={(WorkspaceState) => <Toolbar {...WorkspaceState} />}
>
{(WorkspaceState) => <SomeComponentForContent />}
</Workspace>
)
}
I have a component showing the list of blogpost and I create a URL for every post coming from the title, into a string and then split and joint, but I need to create a second component that render the page of the blogpost itself,
my question is, is there any way to pass the state or props from the already created list component into the blogpost component and be access only by that rout URL?
this is the blogdata component that i wanted to use to transfer the state to the other components but only one is working, fetching the data from the API into this.state.blogpost
class BlogData extends Component{
state = {
blogData: []
}
componentDidMount() {
axios.get(`data.json`)
.then(res => {
const blogData = [res.data.blogPosts] ;
this.setState({blogData});
})
};
render(){
const blogData = this.state.blogData.map((value) =>
value.map((val, idx) =>
<BlogBlock
link={val.title.toString().toLowerCase().split(" ").join("-")}
title={val.title}
subtitle={val.subtitle}
thumb={val.thumb}
idx={idx}
/>
)
)
return(
<section id="blog" className="blogList">
<h2>From the Blog</h2>
<div className="blogPosts">
{blogData}
</div>
</section>
)
}
}
const BlogBlock = (props) => {
return (
<div className={`blog-post post-${props.idx}`}>
<Link to={props.link}>
<Img loader={<span className="loading-img">loading....</span>} src={`http://brittdepetter.com/images/${props.thumb}`} alt={props.title} />
<h3>{props.title}<span><br />{props.subtitle}</span></h3>
</Link>
</div>
)
}
and the component that im trying to create but not working is this one, but no lock of making the route works :(
const BlogPost = (props) => {
return (
<div>
<Router path={props.link} />
<Title title={props.title } />
<div className="textBox boxImg">
<div className="story-img-box">
<Img src={props.imgsrc } />
</div>
<div>
<Paragraph body={props.body} />
</div>
</div>
</div>
)
}
There is way to pass a extra parameter on Link. For that you should pass to attribute as object instead of string.
change your Link
<Link to={props.link}>
to
<Link to={{pathname:props.link, blog:props}}>
In your BlogPost component, access using following code
this.props.location.blog
PS. refreshing browser while you are on BlogPost component will require to re-fetch a blog again.
"Using the React Children API" code example is not working, tried several syntax options, seems the problem is not quite clear.
http://developingthoughts.co.uk/using-the-react-children-api/
class TabContainer extends React.Component {
constructor(props) {
super();
this.state = {
currentTabName: props.defaultTab
}
}
setActiveChild = (currentTabName) => {
this.setState({ currentTabName });
}
renderTabMenu = (children) => {
return React.Children.map(children, child => (
<TabMenuItem
title={child.props.title}
onClick={() => this.setActiveChild(child.props.name)}
/>
);
}
render() {
const { children } = this.props;
const { currentTabName } = this.state;
const currentTab = React.Children.toArray(children).filter(child => child.props.name === currentTabName);
return (
<div>
{this.renderTabMenu(children)}
<div>
{currentTab}
</div>
</div>
);
}
}
When I changed code like this, it compiles finally
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
const TabMenuItem = ({ title, onClick }) => (
<div onClick={onClick}>
{title}
</div>
);
class TabContainer extends React.Component {
constructor(props) {
super();
this.state = {
currentTabName: props.defaultTab
}
}
setActiveChild = ( currentTabName ) => {
this.setState({ currentTabName });
}
renderTabMenu = ( children ) => {
return React.Children.map(children, child => (
<TabMenuItem
title={child.props.title}
onClick={() => this.setActiveChild(child.props.name)}
/>
))
}
render() {
const { children } = this.props;
const { currentTabName } = this.state;
const currentTab = React.Children.toArray(children).filter(child =>
child.props.name === currentTabName);
return (
<div>
{this.renderTabMenu(children)}
<div>
{currentTab}
</div>
</div>
);
}
}
ReactDOM.render(<TabContainer />, document.getElementById("root"));
Not quite experienced with JS and React, so my questions:
1) should this.setActiveChild be used as this.props.setActiveChild?
2) renderTabMenu = ( children ) or renderTabMenu = ({ children })
3) how to fill this page with some content? I don't see any physical children actually present =)
4) don't get the point why bloggers put the code with errors or which is difficult to implement, very frustrating for newcomers
5) any general guidance what can be not working in this example are welcome
Using React.Children or this.props.children can be a bit of a level up in your understanding of React and how it works. It'll take a few tries in making a component work but you'll get that aha moment at some point. In a nutshell.
this.props.children is an array of <Components /> or html tags at the top level.
For example:
<MyComponent>
<h1>The title</h1> // 1st child
<header> // 2nd child
<p>paragraph</p>
</header>
<p>next parapgraph</p> // 3rd child
</MyComponent>
1) should this.setActiveChild be used as this.props.setActiveChild?
Within the TabContainer any functions specified within it need to be proceeded with this. Within a react class this refers to the class itself, in this case, TabContainer. So using this.setActiveChild(). will call the function within the class. If you don't specify this it will try to look for the function outside of the class.
renderTabMenu = ( children ) or renderTabMenu = ({ children })
renderTabMenu is a function which accepts one param children, so call it as you would call it as a normal function renderTabMenu(childeren)
How to fill this page with some content? I don't see any physical children actually present =)
Here's where the power of the TabsContainer comes in. Under the hood, things like conditional rendering happen but outside of it in another component you specify the content. Use the following structure to render home, blog, and contact us tabs.
<TabsContainer defaultTab="home">
<Tab name="home" title="Home">
Home Content
</Tab>
<Tab name="blog" title="Blog">
Blog Content
</Tab>
<Tab name="contact" title="Contact Us">
Contact content
</Tab>
</TabsContainer>
I know how hard it is to make some examples work especially when you are starting out and are still exploring different concepts that react has to offer. Luckily there's stack overflow :).
Here's real live example to play around with, visit this CodeSandBox.
I may be over thinking this, but I am curious if importing a child component directly is bad practice with regards to coupling and testing.
Below is a simple example:
import Header from './header.jsx';
class Widget extends React.Component {
render() {
return (
<div>
<Header></Header>
<div>{this.props.importantContent}</div>
</div>
)
}
}
To me it looks like there is now coupling between Widget and Header. With regards to testing, I don't see an easy way to mock the Header component when testing the Widget component.
How do other larger React apps handle cases like this? Should I pass Header in as a prop? If using react-redux, I can inject header with the Connect method like below to reduce boilerplate. Is that sound?
import { connect } from 'react-redux';
import Header from './header.jsx';
class Widget extends React.Component {
render() {
return (
<div>
{this.props.header}
<div>{this.props.importantContent}</div>
</div>
)
}
}
const mapStateToProps = state => {
return {
header: Header
}
}
export default connect(mapStateToProps)(Widget)
I am interested is simple doing what the community is generally doing. I see that one solution is doing shallow rendering to test on the main part of the component and not the child components using something like Enzyme.
Thoughts or other ideas?
Passing elements / components as props is a good idea. Having default props is a good idea too:
const Widget = ({
header = <div>Default Header.. </div>,
content = <div>Default Content.. </div>
}) =>
<div>
{header}
{content}
</div>
Then elsewhere in your app:
<Widget header={<Header title="Foo" />} content="content from props" />
No need to inject using connect
You can also pass a component, not just an element if you want to interact with props / send data back to parent:
const Widget = ({
Header = props => <div>Default Header.. </div>,
Content = props => <div>Default Content.. </div>
}) =>
<div>
<Header />
<Content />
</div>
Elsewhere:
<Widget Header={Header} Content={props => <Content />} />
As long as the component always renders the same thing it can be directly rendered as a child rather than the parent.
If all other portions of the Component remain constant and only the Header can be different across pages then you could actually implement it as an HOC instead of passing it as a props
const MyCompFactory = ({CustomHeader = DefaultHeader}) => {
return class Widget extends React.Component {
render() {
return (
<div>
<CustomHeader/>
<div>{this.props.importantContent}</div>
</div>
)
}
}
}
and use it like
const CustomComponent = MyCompFactory({CustomComponent: Header})
as long as testing is concerned in your case, you could just shallow render your component and then Search if the Header component is rendered something like
import Header from 'path/to/header'
const component = shallow(
<Widget {...customProps}/>
)
test('test' , () => {
expect(component.find(Header).exists()).toBe(true)
})
I want to automate adding a <Layout /> container component to all my <View /> components.
In this scenario, the Layout component doesn't know which View it is going to get, so I can't apply Relay.createContainer to the Layout since I don't have the relay container spec at declaration.
Because the View is wrapped by the Layout, the RelayRootContainer.component receives the Layout component, which is a non-relay container, and of course, it throws an error.
This is my Layout component:
export default class MainLayout extends React.Component {
render() {
return (
<div>
<Header />
{this.props.children}
<Footer />
</div>
);
}
}
After adding the Relay capabilities to the View, it now looks like this.
class HomeView extends React.Component {
render() {
<ul>
{this.props.store.posts.map(post =>
<li key={post.id}>
{post.name}
</li>
}
</ul>
}
}
const RelayHomeView = Relay.createContainer(HomeView, {
fragments: {
store: () => Relay.QL`
fragment on Store {
posts {
id,
name,
}
}
`
}
});
export default RelayHomeView;
I came up with two possible work-arounds:
Use a high order component Layout and declare the Relay Container Specs as a static property in the View. However, this breaks the convention of exporting an already built relay container
Wrap the render View in the Layout component. Unfortunately this adds a lot of boilerplate by having to import and wrap all views in a component