Dynamic page render reactjs and calling functions from the template - reactjs

I have several components. The main component is the PageTemplate.js, which describes the menu, search bar and site header. And also a place in the center for the content of other pages(functional components react).
The idea is to use the template(PageTemplate.js) on all pages of the site, since there is only one. And generate a block with content from other components (pages, ex. EditRecord.js, NewRecord.js etc).
I use a react-router and when going to /EditRecord, for example, my content block rendered edit's page(EditRecord.js) there
Now I have done it something like this:
Page transitions work and the content block generates the page I need.
but i have a huge problem. For example there is a page with a table with data(MainPage.js). And the search and search function are located in the template(PageTemplate.js). How can I transfer the state from the template(PageTemplate.js) to the page(MainPage.js) to change the data table there by search value from template? And also, if I, for example, will be on the other page(example on /NewRecord) and call teh search function in PageTemplate, then how can I tell the react, that now it is necessary to draw the main page(/) MainPage with new data by searched results?
Many thanks to all in advance;)
router code:
import { Main} from './pages/MainPage/Main';
import { EditRecordPage } from './pages/EditRecordPage/EditRecordPage';
import { NewRecord } from './pages/NewRecordPage/NewRecord';
export const routes = [
{
path: '/',
exact: true,
component: Main,
},
{
path: '/EditRecord/:id',
exact: true,
component: EditRecordPage,
},
{
path: '/NewRecord/',
exact: true,
component: NewRecord,
}
];
Code one of pages (they are identical except for the name):
import React, { useState, useEffect } from 'react';
import { PageTemplate } from '../../components/PageTemplate';
import { MainPage } from './MainPage';
export const Main = (props) => {
return (
<PageTemplate title={'Main page'} {...props}><MainPage/></PageTemplate>
)
}
Here is a chunk in the render of PageTemplate.js where the page(main, edit etc) is rendered:
<Content
className="site-layout-background"
style={{
padding: 24,
margin: 0,
minHeight: 280,
}
>
<div {...props}/>//here is the required page(main, edit, new etc)
</Content>

Related

Gatsby dynamic routes: 404 instead of component, can't configure

I made a simple site with Gatsby.js and can't configure dynamic routes.
I have index.js page (was automatically created by react), that looks like:
import * as React from 'react'
const IndexPage = () => {
return (
<Layout
pageTitle="Home Page"
>
Some text for my main page
</Layout>
)
}
export const Head = () => <title>Home Page</title>
export default IndexPage
Layout components includes Header that looks like:
import React, { useState, useEffect } from 'react';
import { Link } from 'gatsby';
const Header = () => {
return (
<Wrapper style={{ *some styles* }}>
<Link to="/">Index</Link>
<Link to="/projects">Projects</Link >
</Wrapper>
)
};
export default Header;
I have my Projects page that looks like this:
import * as React from 'react'
import { BrowserRouter, Route, Routes } from 'react-router-dom'
import Layout from '../layout'
const Projects = () => {
return (
<BrowserRouter>
<Layout>
<Wrapper>
<Routes>
<Route path="projects/:projectID/" component={ProjectDetails} />
</Routes>
<MyProjectLink to="/projects/1">
Project 1
</MyProjectLink>
<MyProjectLink to="/projects/2">
Project 2
</MyProjectLink>
</Wrapper>
</Layout>
</BrowserRouter>
)
}
export const Head = () => <title>Our Projects</title>
export default Projects
And I have my ProjectDetails component:
import React from 'react';
import { useParams } from 'react-router';
import Layout from '../../pages/layout';
const ProjectDetails = () => {
const { projectID } = useParams();
return (
<Layout>
<Wrapper>
<h2>Project {projectID}</h2>
</Wrapper>
</Layout>
);
}
export default ProjectDetails;
The problem is that when I navigate to localhost:8000/projects/1 (or "2", or "3", or "100500") I see a 404 page instead of my ProjectDetails component.
I've tried wrapping the index page to BrowserRouter and move the routes with my route there, but that's a dumb idea in my opinion (and it doesnt work).
Did I miss something? Some features of Gatsby (v5) that I don't know about? I'm new to Getsby and to be honest I thought that dynamic routes here work the same way as in React Router.
Gatsby extends its routing from React, however, the way to create routes is slightly different.
As far as I understand your code, you are trying to create a template page for projects: this can be simply done by creating a file inside /templates folder. A simple component like this should work:
const Projects = ({ data }) => {
return (
<Layout>
<Wrapper></Wrapper>
</Layout>
);
};
export const Head = () => <title>Our Projects</title>
export default Projects
This template, as long as you use it when creating pages (using either gatsby-node.js or File System Route API) will be used to hold each specific project data.
Each project data will be queried using GraphQL and held inside props.data but without knowing your source (can be markdown, JSON, CMS, etc) I can't provide a sample query.
Once Gatsby infers its GraphQL nodes from your data source, you can use it to get all projects, a specific project, or any other GraphQL data you need on any page/template (page query) or even using static queries.
The idea should be similar to:
// gatsby-node.js
projects.forEach(({ node }, index) => {
createPage({
path: node.fields.slug,
component: path.resolve(`./src/templates/project.js`),
context: {
title: node.title,
},
})
})
In your gatsby-node.js (or File System Route API) you get all projects, loop through them and createPage for each project. The path (URL) for each project will be the slug field (node.fields.slug) but you can use whatever you want. Gatsby will create dynamic pages based on this field.
Then you decide which component will be used as a template: path.resolve(`./src/templates/project.js`) in this case and finally, you populate the context to add a unique value (title in this case: again, this can be an id, the slug, etc). This value will be used to filter the node in the template.
In your Project template:
export const query = graphql`
query ($title: String) {
mdx(title: {eq: $title}) {
id
title
}
}
`
In this case, I'm using markdown-based sources (that's why the mdx node) and this node is filtered by the title ($title) using the context value. The data will be inside props.data of the template. Again, if you want to fetch all projects you will have available an allMarkdown or allMdx (or allJSON...) depending on the source node)

How to load only needed CSS per component (React js)

I've started to code my first React app and it's awesome, but I can't figure out how to manage css files per-component(so the actual CSS won't load if it is not necessary).
React with webpack(correct me if I'm wrong please) wraps the project in such a way that at every given moment the app loads only what it needs(in terms of JS).
So if I have my main App component with only two buttons visible: btn-01 and btn-02, and inside of this component I have another two: component-01 and component-02, and they are hidden till the corresponded button is clicked(btn-01 for component-01), these components won't be loaded until the actual button is clicked(am I getting this right?), however this is not the same with css as I can tell, because I see the css of each of these(component-01 and component-02) components loaded right away the App is loaded, even though none of the buttons are clicked.
I'm not a big fan of inline styling, but I did test it with css module, but the result is the same in this aspect. So I'm not even sure if this is possible to implement in an easy way.
Here's a code, so perhaps I'm not implementing it correctly, but please don't mind the none-DRY code etc.
So as you may see, the style of Component-01 and -02 are loaded even though there is no need for them at the moment(none of the button is pressed).
App.js
import React, { Component } from "react";
import "./App.css";
import Component_01 from "./Component-01/Component-01";
import Component_02 from "./Component-02/Component-02";
class App extends Component {
state = {
isComponent_01: false,
isComponent_02: false,
};
toggleComponent01 = () => {
this.setState({
isComponent_01: !this.state.isComponent_01,
});
};
toggleComponent02 = () => {
this.setState({
isComponent_02: !this.state.isComponent_02,
});
};
render() {
let showComponent_01 = null;
if (this.state.isComponent_01) {
showComponent_01 = <Component_01 />;
}
let showComponent_02 = null;
if (this.state.isComponent_02) {
showComponent_02 = <Component_02 />;
}
return (
<div className="App">
<button className="btn-01" onClick={this.toggleComponent01}>
Btn-01
</button>
<button className="btn-02" onClick={this.toggleComponent02}>
Btn-02
</button>
{showComponent_01}
{showComponent_02}
</div>
);
}
}
export default App;
Component-01.js (and Component-02.js, just with -02.js)
import React from "react";
import style from "./Component-01.module.css";
function App() {
return <div className={style["component-01"]}>Component-01</div>;
}
export default App;

How to import images dynamically to set as the url path for background image property in react

I have an assets folder that store all my images.
And in the assets folder I also have a json file that has objects each has a key that holds the relative path to an image.
[
{"url":"../assets/image1"},
{"url":"../assets/image1"}
]
I want to be able to map through these objects in the json file and dynamically make a component that will have a div where its background-image property will be a url() and the argument of the path will be supplied through each json object.
like this:
function Box(props) {
const style = {
backgroundImage: `url(${props.imageURL})`
}
return(<div style={style}></div>)
}
It works if I specifically import the image path like this, at the top
import imageURL from '../assets/image1';
...
backgroundImage: `url(${imageURL})`
...
But then that way it won't be dynamic.
How do I import import images dynamically to set as the url path for the background-image property?
if you have a object for mapping all images url, maybe can you do this like so?
your map file
import bg from './bg.jpg';
import bg1 from './bg1.jpg';
export default {
bg,
bg1,
...
}
your component
import imageMap from '../assets/imageMap';
function Box() {
const [imageUrl, setImageUrl] = React.useState(imageMap.bg);
React.useEffect(() => {
/* you can change image url by effects dynamically */
}, [])
/* you can change image url by events dynamically too */
const handleClick = () => {
setImageUrl(imageMap.bg1);
}
return (
<div
style={{
backgroundImage: `url(${imageUrl})`
}}
onClick={handleClick}
/>
)
}
Answer inspired by #chonnychu
I ended up making a component that handles all the imports, and exporting all the imported stuff at the end.
While this is not the optimal solution, but it's an acceptable amount of manual work.
import box1image from "../assets/TimeSeriesAnalysis.png";
const boxInfo = [
{
id: 1,
image: box1image,
title: "Box 1",
description:
"Box 1 is the first box",
},
];
export default boxInfo;

How to render the menu from the routes defined at static.config.js file in an application build by react-static?

Following the official guide, it should be something simple like this:
//App.js
import React from 'react'
import { useRouteData } from 'react-static'
const { myRoutes} = useRouteData()
function App() {
console.log(myRoutes.length)
return (
<div>
<h1>My page list</h1>
<ul>
{myRoutes.map(item => (
<li key={item.path}>{item.template}</li>
))}
</ul>
</div>
)
}
export default App
My static.config.js file bellow was cut from the basic template. Only have a route to About page:
//static.config.js
import path from 'path'
export default {
getRoutes: async () => {
return [
// A simple route
{
path: 'about',
template: 'src/pages/about',
},
]
},
plugins: [
[
require.resolve('react-static-plugin-source-filesystem'),
{
location: path.resolve('./src/pages'),
},
],
require.resolve('react-static-plugin-reach-router'),
require.resolve('react-static-plugin-sitemap'),
],
}
But nothing is rendered.
if i set myRoutes variable inline it works. Example:
const myRoutes = [{"path":"p1","template":"t1"}]
It seams the function useRouteData() is not called but i can't figure it out why.
The site is all made of static pages, available from any page in the site, so i think if i put all in the configuration file it will slit code, one page per template and also provide information to build the menu. I am not familiar with static-react build too, so may not be using the template as i am supposed too.

react-navigation Screen that conceals TabBar from nested StackNavigator

I'm new to react-navigation and trying to wrap my head around how to do the following:
Given this navigation structure:
RootTabNavigator
LoggedOut_StackNavigator
...
LoggedIn_StackNavigator
LoggedIn_TabNavigator <-- TabBar rendered by this Navigator
TabA_StackNavigator
ScreenA
ScreenB
I would like to be able to navigate from ScreenA to ScreenB using the typical "slide in from right" transition, in such a way that the TabBar is visible on ScreenA, but is not visible on ScreenB. In other words, when I navigate to ScreenB, I want it to take up the entire window.
Once the user transitions from ScreenA to ScreenB, they can either press the back button to return back to ScreenA, or navigate to new routes using the same transition with the TabBar still not visible.
What I've tried:
navigationOptions.tabBarVisible: this property only seems to work when applied to TabA_StackNavigator itself, which means that all of the screens in its stack also conceal the TabBar. Adding it to the screens inside the StackNavigator has no effect.
Adding a new AllScreens_StackNavigator as a sibling of LoggedIn_TabNavigator and navigating to routes inside this navigator, I get the error: Expect nav state to have routes and index, {"routeName":"ScreenB", "params": {}, "key": "init-id-1516..."}. The navigation action I dispatched to try to do this:
{
"action": Object {
"params": Object {},
"routeName": "ScreenB",
"type": "Navigation/NAVIGATE",
},
"params": Object {},
"routeName": "AllScreens_StackNavigator",
"type": "Navigation/NAVIGATE",
}
Any help is greatly appreciated!
Edit: this answer is relevant to react-nagivation v1.~ (pre v2.0)
As suggested in the comments, see this issue:
https://github.com/react-navigation/react-navigation-tabs/issues/19
Apparently, the navigationOptions of an inner component affect the containing navigator's parent navigator as well.
Solution
That means this code should work for you:
class ScreenB extends React.Component {
static navigationOptions = {
header: () => null, //this will hide the Stack navigator's header (TabA_StackNavigator)
tabBarVisible: false //this will hide the TabBar navigator's header (LoggedIn_TabNavigator)
}
Explanation
First, you can set the navigation options per individual screen (component). You can see how in the code snippet above or here: React Navigation - Screen Navigation Options
Second, you tried:
Adding it to the screens inside the StackNavigator has no effect.
It didn't work because hiding the StackNavigator's header requires setting the header field to null.
From the React Navigation documentation:
React Element or a function that given HeaderProps returns a React
Element, to display as a header. Setting to null hides header
Third, using tabBarVisible is actually correct, but it affects only the TabNavigator. And to make it disappear only for one tab and not for all the tabs, you need to set it on the specific screen. ScreenB in your case.
Hope this helps!
The following is what ended up working for me, so I'm posting it the hopes that it helps others. I haven't had a chance to try #talzaj's implementation so I'll leave it up to others to upvote whatever works best for them. The following solution has been working well for me, including inside nested navigators.
I updated my navigation structure such that:
LoggedIn_StackNavigator still has LoggedIn_TabNavigator as one of its screens, and this LoggedIn_TabNavigator is the initial route of LoggedIn_StackNavigator as set using initialRouteName.
LoggedIn_StackNavigator also contains a route for every screen that will ever need to be shown full screen and conceal the tab bar. (If you are re-using screens, where some are shown with the tab bar visible and others where it is not, make sure to use unique keys for routes that re-use the same screen.
Navigation Structure
So, the navigation structure looks like:
RootTabNavigator
LoggedOut_StackNavigator
LoggedIn_StackNavigator
ScreenA // ( reuse screen component, different route key )
ScreenB // ( reuse screen component, different route key )
LoggedIn_TabNavigator <-- TabBar rendered by this Navigator
TabA_StackNavigator
ScreenA
ScreenB
LoggedIn_StackNavigator:
And LoggedIn_StackNavigator looks like:
import { StackNavigator } from 'react-navigation';
import LoggedIn_TabNavigator from './LoggedIn_TabNavigator';
import {
ScreenA,
ScreenB,
} from './LoggedIn_TabNavigator/TabA_StackNavigator/Screens';
const LoggedIn_StackNavigator = StackNavigator({
WithoutTabBar_ScreenA: {
screen: ScreenA
},
WithoutTabBar_ScreenB: {
screen: ScreenB
},
LoggedIn_TabNavigator: {
screen: LoggedIn_TabNavigator
}
}, {
initialRouteName: 'LoggedIn_TabNavigator'
});
export default LoggedIn_StackNavigator;
From there, I wrote a helper HOC for pushing full screen routes:
import React from 'react';
import { withNavigation } from 'react-navigation';
import { fullScreenRoutePrefix } from './somewhere';
export default function withNavigateFullScreen(Child) {
#withNavigation
class WithNavigateFullScreenHOC extends React.Component {
navigateToFullScreenRoute = (routeName, params) => {
this.props.navigation.navigate(
`${fullScreenRoutePrefix}${routeName}`, params
);
}
render() {
return (
<Child
{...this.props}
navigateFullScreen={this.navigateToFullScreenRoute}
/>
);
}
}
return WithNavigateFullScreenHOC;
}
And then I can navigate to full screen routes like so:
import React from 'react';
import { withNavigateFullScreen } from 'components/higher-order';
import { Text } from 'react-native';
#withNavigateFullScreen
export default class ScreenA extends React.Component {
goToScreenB = () => {
this.props.navigateFullScreen('ScreenB');
}
render() {
return <Text onPress={this.goToScreenB}>Go To Screen B</Text>;
}
}

Resources