SSR with data fetch without state management library - javascript

I am trying to make a SSR react app, but not able to pass props from express to the component.
What mistake am i doing?
server.js
import AppPage2 from './src/project02/LandingPage';
......
......
server.get('/project2',async (req,res)=>{
const context = {data:'test'}
const sheet = new ServerStyleSheet();
const content = ReactDOMServer.renderToString(sheet.collectStyles(
<StaticRouter location={req.url} context={context}>
<AppPage2 state={{"foo":"baar"}}/>
</StaticRouter>)
);
const styles = sheet.getStyleTags();
let html = appShell( 'Project 2', content,' project2.bundle.js',styles)
res.status(200).send(html);
res.end()
})
AppPage2(./src/project02/LandingPage)
import React from 'react';
import {Wrapper,Title} from './styleComponent/testStylePage'
.......
.......
class AppPage extends React.Component {
componentDidMount() {
console.log("{{API}}",this,this.props, this.props.staticContext)
}
render(){
return(
<Wrapper>
<Title>This is project 01 </Title>
</Wrapper>
)
}
}
export default AppPage;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import AppPage from './project02/LandingPage'
ReactDOM.hydrate(
<AppPage></AppPage>,
document.querySelector('#root')
);
webpack.client.conf
const path = require('path');
const glob = require("glob");
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const entry = glob.sync("src/*.js")
.reduce((x, y) => Object.assign(x,
{
[y.replace('src/','').replace('.js','')]: `./${y}`,
}
), {});
module.exports = {
target: 'node',
mode: 'development',//development,production
entry: entry,
output: {
filename:'[name].bundle.js',
path: path.resolve(__dirname,'build/public/'),
publicPath: '/build/'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader'
},
},
]
},
plugins: [
// new BundleAnalyzerPlugin()
]
}
I am not able to log console.log("{{API}}",this,this.props, this.props.staticContext) from AppPage2(./src/project02/LandingPage) this page i am sending data from server.js(express)

Your props already passed to your component on Page2, but you're longing it using a function that will never been called,
componentDidMount() is invoked immediately after a component is mounted (inserted into the DOM tree).
in your case, you are not mounting any thing to the DOM, because react will only render your component to html and will not wait for your component to Mount, practically there is no DOM in your NodeJs server, and you're only rendering the component and return it as string, not inserting it to the DOM.
Your props are already there and you can console log them in your class constructor, and in your render method:
class AppPage extends React.Component {
constructor (props){
super(props)
console.log("{{API}}",this,this.props, this.props.staticContext)
}
render(){
//or console log it here
console.log("{{API}}",this,this.props, this.props.staticContext)
return(
<Wrapper>
<Title>This is project 01 </Title>
</Wrapper>
)
}
}
in this state your compoenent will mount and not did mount you can also console log your props using UNSAFE_componentWillMount but as it's name said it's unsafe
ReactJS.org
You can also create your own functions and it will works:
myFunction () {
console.log(this.props.state)
}
myFunction();

Related

Using QueryClientProvider in a Wrapper

I want to use a single Wrapper Component in a library im providing. The wrapper should provide stuff like Context and QueryClient. The component looks like this:
import { QueryClient, QueryClientProvider } from "react-query";
const client = new QueryClient();
function Wrapper(props) {
return (
<QueryClientProvider client={client}>
{props.children}
</QueryClientProvider>
);
}
When wrapping children with the Wrapper useQuery throws an error that no QueryClientProvider is set. In this case App uses the useQuery Hook from react-query.
import { Wrapper } from "my-lib";
ReactDOM.render(
<React.StrictMode>
<Wrapper>
<App />
</Wrapper>
</React.StrictMode>,
document.getElementById("root")
);
At first i thought you need a QueryClientProvider directly above an useQuery hook. But for example in Storybook you can define one QueryClient for all stories together. Where did i went wrong?
Edit:
App component:
import { useQuery } from "react-query";
export const App = () => {
const data = useQuery("data", () =>
fetch("https://jsonplaceholder.typicode.com/todos/1").then((res) =>
res.json()
)
);
if (data.isError || data.isLoading) return <>Error/Loading</>;
return <>{data.data.title}</>;
};
Well a few steps lead to my goal:
In the library, put react-query into the devDependencies and peerDependencies.
In the app, put react-query into the dependencies.
In the library, exclude react-query from being bundled by adding it to your bundler config. In my case it's the vite.config.ts:
const path = require("path");
const { defineConfig } = require("vite");
module.exports = defineConfig({
build: {
lib: {
entry: path.resolve(__dirname, "src/index.ts"),
name: "my-lib",
formats: ["es", "umd"],
fileName: "my-lib",
},
rollupOptions: {
external: [
"react",
"react-dom",
"react-query",
],
},
},
});

How do I solve object or props is not defined in ReactJS?

I am using a basic codebase in React that uses Webpack to compile ES6 and JSX to be backwards compatible.
I created a component using ES6 syntax and I want to display a props value into the component but I get an error in the console that says:
Uncaught ReferenceError: myCheese is not defined
at Module../src/index.js (index.js:8)
at webpack_require (bootstrap:18)
at startup:3
at startup:5
App.js
import React from "react";
import { hot } from "react-hot-loader";
class App extends React.Component {
render() {
return (
<div>
<p>Another paragraph</p>
<p>{this.props.msg}</p>
<p>
<strong>Cheese name: </strong> {this.props.cheese.name}
</p>
</div>
);
}
}
var myCheese = {
name: "Camembert",
smellFactor: "Extreme pong",
price: "3:50",
};
export default hot(module)(App);
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import "./styles.css";
// put component into html page
ReactDOM.render(
<App msg="I like cheese" cheese={myCheese} />,
document.getElementById("app")
);
webpack.config.base.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
path: path.join(__dirname, "dist"),
filename: "app.bundle.js",
},
module: {
...
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
}),
],
};
Any help is greatly appreciated. Thanks.
You have myCheese in App.js rather than index.js
Try moving this block of code into index.js
var myCheese = {
name: "Camembert",
smellFactor: "Extreme pong",
price: "3:50",
};

Can't import React component on server-side Next.js

I'm trying to import a very simple component from a library into a Next.js app.
I have stripped down my library to the bare minimum. It contains the following src/components/index.js:
import React from 'react'
const Container = ({ children }) => <div>{children}</div>
export { Container }
In my Next.js app, I created a pages/index.js that contains the following:
import React from 'react'
import { Container } from 'mylib'
class HomePage extends React.Component {
render() {
return <Container>Hello</Container>
}
}
export default HomePage
mylib is a local npm package which I have installed in my Next.js app using npm i ../mylib. It has a standard webpack config:
const path = require('path')
module.exports = {
entry: {
components: './src/components/index.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
libraryTarget: 'commonjs2'
},
mode: "production",
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
}
}
],
},
};
and then the package.json points to dist/components.js as the main file.
The following does work:
In my Next.js app, replace return <Container>Hello</Container> with return <div>Hello</div> (while keeping the import { Container } in place)
Refresh the page in the server (I'm running Next's dev server)
Restore the use of <Container> (the opposite of step 1)
Save the file --> hot-reload in the browser with the Container properly displayed (I added a className to be sure)
At this point, if I refresh the browser, I get the following error:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
which suggests I made a bad import/export, but I don't know where. And the error only occurs on the server-side.
What did I do wrong?
Container should be a default export, so this:
import React from 'react'
const Container = ({ children }) => <div>{children}</div>
export { Container }
should be this:
import React from 'react'
const Container = ({ children }) => <div>{children}</div>
export default Container
You could also one-line the Container component like this:
import React from 'react'
export default Container = ({ children }) => <div>{children}</div>

React: Make components external

I've been trying to get into React and therefore read the official guides, however, I'm not sure how to put components into their own files to reuse them across files.
I've made a very basic React Component which holds an input tag and some configuration with it. I would like to make this input tag a standalone component in its own file. How do I achieve this?
I use Webpack with this configuration:
var path = require('path');
var webpack = require('webpack');
module.exports = {
devtool: 'eval',
entry: [
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
'./src/index'
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/static/'
},
plugins: [
new webpack.HotModuleReplacementPlugin()
],
module: {
loaders: [{
test: /\.js$/,
loaders: ['react-hot', 'babel'],
include: path.join(__dirname, 'src')
}]
}
};
This is the React code:
import React, { Component } from 'react';
export default React.createClass({
getInitialState: function() {
return {text: ''};
},
handleChange: function(e) {
this.setState({text: e.target.value})
},
render() {
return (
<div>
<h1>What's your name?</h1>
<input
type="text"
value={this.state.text}
onChange={this.handleChange}
/>
<h2>Hallo, {this.state.text}</h2>
</div>
);
}
});
Edit: I forgot the index.js:
import React from 'react';
import { render } from 'react-dom';
import App from './Components/App';
render(<App />, document.getElementById('root'));
Thanks.
You will want to create an input component and pass your state and handle change function as props to the component.
Input Component :
import React, {Component} from 'react';
var Input = React.createClass({
handleChange: function(e) {
this.props.handleChange(e.target.value);
}
render() {
return (
<input
type="text"
value={this.props.text}
onChange={this.handleChange} />
)
}
});
export default Input;
Your current component (with the input component added in):
import React, { Component } from 'react';
import Input from './path_to_inputComponent';
export default React.createClass({
getInitialState: function() {
return {text: ''};
},
handleChange: function(text) {
this.setState({text: text})
},
render() {
return (
<div>
<h1>What's your name?</h1>
<Input text={this.state.text} handleChange={this.handleChange} />
<h2>Hallo, {this.state.text}</h2>
</div>
);
}
});
So what will happen is, this.state.text will be passed as the text prop to Input which will set the value of the input. This will allow the input component to be used in multiple places and have a different value in each place that it is used. Also, when the onChange event is fired from the input, it will call the function that handleChange function that is passed to it as a prop. Therefore, it will call the function in the parent component and the state will be updated in the parent component.

ReactJS - Element type is invalid error

I have the following error with the below code, however I cannot identify what is wrong with my component. Any help is appreciated!
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. Check the render method of App.
index.js
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import YTSearch from 'youtube-api-search'; // Used to interact with Youtube API
import SearchBar from './components/search_bar'; // custom search bar component
import VideoList from './components/video_list'; // custom youtube video list component
const API_KEY = "key not shown here for privacy purposes";
class App extends Component {
constructor(props){
super(props);
this.state = {videos: []};
YTSearch({key: API_KEY, term: 'sufboards'}, (videos) => {
this.setState({videos});
});
}
render(){
return (
<div>
<SearchBar />
<VideoList videos={this.state.videos} />
</div>
);
}
}
ReactDOM.render(<App />, document.querySelector(".container"));
webpack.config.js
As requested by JordanHendrix.
module.exports = {
entry: [
'./src/index.js'
],
output: {
path: __dirname,
publicPath: '/',
filename: 'bundle.js'
},
module: {
loaders: [{
exclude: /node_modules/,
loader: 'babel'
}]
},
resolve: {
extensions: ['', '.js', '.jsx']
},
devServer: {
historyApiFallback: true,
contentBase: './'
}
};
video_detail.js
As requested by bhargavponnapalli
import React from 'react';
const VideoList = (props) => {
return (
<ul className="col-md-4 list-group">
{props.videos.length}
</ul>
);
};
export default VideoList;
search_bar.js
As requested by bhargavponnapalli
import React, {Component} from 'react';
class SearchBar extends Component {
constructor(props){
super(props);
this.state = {term: ""};
}
render(){
return (
<input
onChange={
event => this.setState({
term: event.target.value
})
}
value = {this.state.term}
/>
);
}
}
export default SearchBar;
Any extra code needed from my other components will be added here upon request.
Simple fix:
import VideoList from './components/video_list';
The file was empty, my code was in video_detail.js so I had to move it to the correct file, video_list.js

Categories