React - use props with separate files to avoid hardcoding? - javascript

I am new to React. In my Homepage component I want to avoid hardcoding "Hello World" inside my PageHeader by using props. I am trying to declare my prop, title, inside another file, App.js, inside my render function but nothing is showing up. How do I use React efficiently to use props with separate files to avoid hardcoding? What is the best way to do this?
My App.js file:
class App extends Component {
render() {
return (
<div className="App">
<Homepage title="Hello World" />
</div>
);
}
}
export default App;
My Homepage.js file:
const Homepage = (props) => {
return (
<div>
<PageHeader> {props.title} </PageHeader>
</div>
)
}
export default Homepage;
Also, my files are imported correctly.
Note: I am importing PageHeader from React-Bootstrap so when I try to do the answer below it says that it's a duplicate declaration. How can I change my code to avoid this?

You need to pass the props to the child elements explcitly. you could use {...props} as an attribute on the component / use react's props.children to display the content that is been passed within the tags body.
Here are the couple of ways to acheive what you asked for.
Passing props to the children component:
const PageHeader = ReactBootstrap.PageHeader;
const Homepage = props => {
return (
<div>
<PageHeader> {props.title} </PageHeader>
</div>
);
};
class App extends React.Component {
render() {
return (
<div className="App">
<Homepage title="Hello World - 1" />
</div>
);
}
}
ReactDOM.render(<App />, document.querySelector("#app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-bootstrap/0.29.4/react-bootstrap.min.js"></script>
<div id="app"></div>

I guess the answer to your question may be handled by Javascript instead, please take a look at this post: https://medium.freecodecamp.org/how-to-structure-your-project-and-manage-static-resources-in-react-native-6f4cfc947d92
You can review how they manage hardcoded text using a separated file and then importing it into your js files
const strings = {
onboarding: {
welcome: {
heading: 'Welcome',
text1: "What you don't know is what you haven't learn",
text2: 'Visit my GitHub at https://github.com/onmyway133',
button: 'Log in'
},
term: {
heading: 'Terms and conditions',
button: 'Read'
}
}
}
export default strings
You need to place that file into a 'res' folder and then you can import it into any JS file using the res folder relative path:
import strings from '../res/strings'

Related

How to apply css for a class in react component?

I am a new react-js learner and I am having a hard time adding css to my classes that I have inside my react component.
Here is the current code:
import React from 'react';
const Home = () => {
return (
<>
<div class="container">
<h1 class="mainHeader">Home</h1>
<h2>helloo</h2>
</div>
</>
);
};
.container {
// CSS would go here
}
export default Home;
In just HTML and CSS, I was able to apply css on the container div class by just using '.' and whatever the class name was. However, this is giving me an error.
Put the css in its own file, with a .css extension, then import it. Assuming you used create-react-app to set up your project, it will already have appropriate configuration for importing css files. Additionally, you need to use className for the prop, not class
// In a new file home.css:
.container {
// css goes here
}
// In the file you've shown:
import React from 'react';
import './home.css';
const Home = () => {
return (
<>
<div className="container">
<h1 className="mainHeader">Home</h1>
<h2>helloo</h2>
</div>
</>
);
};
export default Home;
Or you can declare it in json format or like you would an object, not in CSS form. Treat it as you are writing in js, which you actually are. See the edit below:
import React from 'react';
const Home = () => {
return (
<>
<div style={container}>
<h1 className="mainHeader">Home</h1>
<h2>helloo</h2>
</div>
</>
);
};
const container = {
// CSS would go here
color: 'red',
background: 'blue'
}
export default Home;

React: Problem in dynamic nested routing. How to solve this error?

I am getting the following error while trying to implement dynamic routing in React JS.
The required files are:
Assignment.js
import React, { Component } from 'react';
import Users from './containers/Users/Users';
import Courses from './containers/Courses/Courses';
import {Route, Link, Switch, Redirect} from 'react-router-dom';
import Course from './containers/Course/Course';
class Assignment extends Component{
render(){
return(
<div>
<ul>
<li><Link to ="/Users"> Users </Link></li>
<li><Link to ="/Courses"> Courses </Link></li>
</ul>
<Switch>
<Route path ="/Users" component = {Users}/>
<Route path ="/Courses" exact component = {Courses}/>
</Switch>
</div>
)
}
};
export default Assignment;
Courses.js
import React, { Component } from 'react';
import {Link, Route} from 'react-router-dom';
import './Courses.css';
import Course from '../Course/Course';
class Courses extends Component {
state = {
courses: [
{ id: 1, title: 'Angular - The Complete Guide' },
{ id: 2, title: 'Vue - The Complete Guide' },
{ id: 3, title: 'PWA - The Complete Guide' }
]
}
render () {
return (
<div>
<h1>Amazing Udemy Courses</h1>
<section className="Courses">
{
this.state.courses.map( course => {
return (<Link key ={course.id} to = {this.props.match.url + '/' + course.id + '/' + course.title}>
<Course className = "Course" name = {course.title} no = {course.id} />
</Link>);
} )
}
<Route path = "/Courses/:id/:name" exact component = {Course} />
</section>
</div>
);
}
}
export default Courses;
Course.js
import React, { Component } from 'react';
class Course extends Component {
render () {
return (
<div>
<h1>{this.props.match.params.name}</h1>
<p>{this.props.match.params.id}_</p>
</div>
);
}
}
export default Course;
Why am I getting this error? Can anyone fix this? I am also finding it difficult to following dynamic routing.
PS. I am getting the error at /Courses url only not at the base url.
Have you tried withRouter?
import { withRouter } from 'react-router-dom';
console.log(props.match.params);
then export the component like:
export default withRouter(TestComponent);
Problems
The props such as params are only passed down to the top-level component which is rendered by a Route. When you are rendering the list of individual Course components inside your Course component, the Courses gets this.props.params but each Course does not. You can pass them down manually:
<Course {...this.props} className="Course" name={course.title} no={course.id} />
The above passes all props, while the below passes just the params prop.
<Course params={this.props.params} className="Course" name={course.title} no={course.id} />
This resolves your error, but it is not at all doing what you want it to be doing. The match is for the current URL, so this.props.match.params.name and this.props.match.params.id are both empty values when we are on the /Courses page. Meanwhile, the props className, name, and no which you set on the Course are all unused.
Additionally, the Route to "/Courses/:id/:name" which you have put inside of Courses should really be on the top level of the app alongside the main "/Courses" Route. Ideally it should be listed before the courses homepage route because you want to match to more specific paths before broader ones, but it won't present any conflicts with exact either way.
There is a lot wrong here and I recommend that you read up on the fundamentals of react-router and writing reusable components.
Rewrites
You are trying to use the same component to render a course for both your Route "/Courses/:id/:name" and as a list item on the Courses page, but one needs to have its data passed directly as props while the other gets its data from this.props.match.params. In order to solve this, we will make a component that handles just the rendering of the course. It gets its information from props, and is agnostic to where those props come from. This means we can use this component on any page of your app as long as we pass it a name and no. I used a function component, but it doesn't matter.
const CourseListItem = ({ name, no }) => {
return (
<div className="course">
<h1>{name}</h1>
<p>Course #{no}</p>
</div>
);
};
We can't send our Route directly to this component, because it wouldn't know the name and no. So we use an intermediate component that is responsible for setting the props of CourseListItem based on the router props (this.props.match.params). You could of course render other HTML or components and not just CourseListItem. I used a class for consistency with what you had before, but again it doesn't matter.
class SingleCoursePage extends Component {
render() {
return (
<CourseListItem
name={this.props.match.params.name}
no={this.props.match.params.id}
/>
);
}
}
In our Courses component, we loop through the courses from this.state and for each course we render the CourseListItem, setting the props name and no from the course object. See how we can use same component in different ways? If you wanted, you could make className be a prop of CourseListItem so that you could style it differently in different places.
class Courses extends Component {
state = {
courses: [
{ id: 1, title: "Angular - The Complete Guide" },
{ id: 2, title: "Vue - The Complete Guide" },
{ id: 3, title: "PWA - The Complete Guide" }
]
};
render() {
return (
<div>
<h1>Amazing Udemy Courses</h1>
<section className="Courses">
{this.state.courses.map((course) => {
return (
<Link
key={course.id}
to={"/Courses/" + course.id + "/" + course.title}
>
<CourseListItem name={course.title} no={course.id} />
</Link>
);
})}
</section>
</div>
);
}
}
As I explained, we are moving that Route for the single course page up to the top-level component, alongside the other routes.
class Assignment extends Component {
render() {
return (
<BrowserRouter>
<div>
<ul>
<li><Link to="/Users">Users</Link></li>
<li><Link to="/Courses">Courses</Link></li>
</ul>
<Switch>
<Route path="/Users" component={Users} />
<Route path="/Courses/:id/:name" component={SingleCoursePage} />
<Route path="/Courses" exact component={Courses} />
</Switch>
</div>
</BrowserRouter>
);
}
}
Code Sandbox Link - There's no CSS styling but all of the routing works!

How to share state between child component (siblings) in ReactJS?

I would like to pass state to a sibling or even a grandparent whatever.
I have 3 components. Inside Header, I have a button with an onClick function to toggle a Dropdown Menu inside Navigation. And by the way, I would like to pass the same state to AnotherComponent.
How to pass state (such as isDropdownOpened) from Header to Navigation and AnotherComponent?
<div>
<Header />
<Navigation />
<div>
<div>
<div>
<AnotherComponent />
</div>
</div>
</div>
</div>
You have different approaches to address this situation.
Keep the state in the top component and pass it to children through props
Use a state container to keep and share your application state among components (e.g. https://redux.js.org/)
Use the new React Context feature. Context provides a way to pass data through the component tree without having to pass props down manually at every level.
That's the exact reason why "React Hooks" have been developed (and hyped by the community 😉), but don't use them yet in production, they are still in early development (alpha) and their specification/implementation might be changed!
You problem can be solved using the awesome “React Context“ API which allows to pass data to components no matter how deep they are nested in the tree.
To get to know to context read the extensive documentation linked above. I'll only explain a small and quick example here:
Create a context component and export the consumer
App.jsx
import React from "react";
// The initial value can be anything, e.g. primitives, object, function,
// components, whatever...
// Note that this is not required, but prevebents errors and can be used as
// fallback value.
const MyContext = React.createContext("anything");
// This component is the so called "consumer" that'll provide the values passed
// to the context component. This is not necessary, but simplifies the usage and
// hides the underlying implementation.
const MyContextConsumer = MyContext.Consumer;
const someData = { title: "Hello World" };
const App = ({ children }) => (
<MyContext.Provider value={someData}>{children}</MyContext.Provider>
);
export { MyContextConsumer };
export default App;
Import the created consumer in any component and use the provided value
AnotherComponent.jsx
import React from "react";
import { MyContextConsumer } from "./App";
const AnotherComponent = () => (
<div>
<MyContextConsumer>{({ title }) => <h1>{title}</h1>}</MyContextConsumer>
</div>
);
export default AnotherComponent;
Render the app with both context components
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import AnotherComponent from "./AnotherComponent";
const Root = () => (
<App>
<AnotherComponent />
</App>
);
const rootElement = document.getElementById("root");
ReactDOM.render(<Root />, rootElement);
The component will render a level 1 heading with the "Hello World" text.
How to pass state (such as isDropdownOpened) from Header to Navigation and AnotherComponent, please ?
You hold the state in an ancestor of Header and pass that state to Haeader, Navigation, and AnotherComponent as props. See State and Lifecycle and Lifting State Up in the documentation.
Example:
const Header = props => (
<div>
<span>Header: </span>
{props.isDropdownOpened ? "Open" : "Closed"}
</div>
);
const Navigation = props => (
<div>
<span>Navigation: </span>
{props.isDropdownOpened ? "Open" : "Closed"}
</div>
);
const AnotherComponent = props => (
<div>
<span>AnotherComponent: </span>
{props.isDropdownOpened ? "Open" : "Closed"}
</div>
);
class Wrapper extends React.Component {
constructor(props) {
super(props);
this.state = {
isDropdownOpened: false
};
}
componentDidMount() {
setInterval(() => {
this.setState(({isDropdownOpened}) => {
isDropdownOpened = !isDropdownOpened;
return {isDropdownOpened};
});
}, 1200);
}
render() {
const {isDropdownOpened} = this.state;
return (
<div>
<Header isDropdownOpened={isDropdownOpened} />
<Navigation isDropdownOpened={isDropdownOpened} />
<div>
<div>
<div>
<AnotherComponent isDropdownOpened={isDropdownOpened} />
</div>
</div>
</div>
</div>
);
}
}
ReactDOM.render(
<Wrapper />,
document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
There are some other options, which Arnaud usefully provides in his answer.
Like how TJ Said, use the state of the parent component. That way one state is shared by all the sub components, which is what you wanted I presume.
class ExampleParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
isDropdownOpened: false
}
}
toggleDropdown() {
this.setState({
isDropdownOpened: !isDropdownOpened
});
}
render() {
return (
<div>
<Header open={isDropdownOpened} toggleDropdown={ this.toggleDropdown }/>
<Navigation open={ isDropdownOpened}/>
<div>
<div>
<div>
<AnotherComponent open={ isDropdownOpened} />
</div>
</div>
</div>
</div>
);
}
class Header extends React.Component {
render() {
return (
<div>
<button onClick={ this.props.toggleDropdown }>TOGGLE ME</button>
{ isDropdownOpened && (
<h1> DROPPED </h1>
}
</div>
);
}
}
You can only use this.state.variableName to access
<ChildComponent data={this.state.name} />
And to pass functions
<ChildComponent data={this.HandleChange} />
First Send the data from the first child to the common parent using callback
function and then send that received data (stored in state in parent component)
to the second child as props.
you can also read this article - https://www.pluralsight.com/guides/react-communicating-between-components

Dynamically rendered Tag is always lowercase

I am trying to output some svgs and output them from a list, here is my render method:
render() {
const renderTag = () => {
const Tag = this.props.id
return(<Tag />)
}
return (
<div key={this.props.name} className="social-box">
<a className={this.props.id + "-link"}>
{renderTag()}
</a>
</div>
)
}
However, the DOM node is always lowercase i.e. <facebook> rather than <Facebook> this.props.id is correctly rendered to the console as Facebook. Can anyone tell me why react or the browser incorrectly renders as lowercase, and therefore not the component, and how to fix?
It's a technical implementation of React, all tags get lowercased on this line here, AFAIK it's not possible to render non-lowercased tags and that is by design.
Read more here.
i suggest that you would take a look at this article about dynamic components.
The most relevant example from the article:
import React, { Component } from 'react';
import FooComponent from './foo-component';
import BarComponent from './bar-component';
class MyComponent extends Component {
components = {
foo: FooComponent,
bar: BarComponent
};
render() {
const TagName = this.components[this.props.tag || 'foo'];
return <TagName />
}
}
export default MyComponent;
you most likely have a limited amount of components that could be rendered, so you might create a dictionary that contain a key (name of the component) to the component itself (as shown in the example) and just use it that way:
import Facebook from './FaceBook';
import Twitter from './Twitter';
const components = {
facebook: Facebook,
twitter: Twitter
};
render() {
return <div key={this.props.name} className="social-box">
<a className={this.props.id + "-link"}>
<components[this.props.id] />
</a>
</div>;
}
I find the answer eventually. #TomMendelson almost had the answer, but it needed fleshing out a bit more.
A function to create the component outside of the render method, suggested by #ShubhamKhatri actually did the job. Here's the final code:
import React from 'react';
import Facebook from './svg/Facebook';
import LinkedIn from './svg/LinkedIn';
import Twitter from './svg/Twitter';
import Pinterest from './svg/Pinterest';
class SocialMediaBox extends React.Component {
renderElement(item) {
const Components = {
'Facebook': Facebook,
'Twitter': Twitter,
'Pinterest': Pinterest,
'LinkedIn': LinkedIn
}
return React.createElement(Components[item], item);
}
render() {
const Element = this.renderElement(this.props.id)
return
(
<div>
{Element}
</div>
)
}
}
export default SocialMediaBox;
Thanks for the question and answers; alongside the answers given in Dynamic tag name in jsx and React they helped me to find a solution in my context (making a functional component in Gatsby with gatsby-plugin-react-svg installed):
import React from "react"
import FirstIcon from "../svgs/first-icon.inline.svg"
import SecondIcon from "../svgs/second-icon.inline.svg"
import ThirdIcon from "../svgs/third-icon.inline.svg"
const MyComponent = () => {
const sections = [
{ heading: "First Section", icon: () => <FirstIcon /> },
{ heading: "Second Section", icon: () => <SecondIcon /> },
{ heading: "Third Section", icon: () => <ThirdIcon /> },
]
return (
<>
{sections.map((item, index) => {
const Icon = item.icon
return (
<section key={index}>
<Icon />
<h2>{item.heading}</h2>
</section>
)
})}
</>
)
}
export default MyComponent
As mine is a Gatsby project I used the above mentioned plugin, but it itself process svgs with svg-react-loader so the basic principle should work in any React project using this package.

Functions are not valid as a React child. This may happen if you return a Component instead of from render

I have written a Higher Order Component:
import React from 'react';
const NewHOC = (PassedComponent) => {
return class extends React.Component {
render(){
return (
<div>
<PassedComponent {...this.props}/>
</div>
)
}
}
}
export default NewHOC;
I am using the above in my App.js:
import React from 'react';
import Movie from './movie/Movie';
import MyHOC from './hoc/MyHOC';
import NewHOC from './hoc/NewHOC';
export default class App extends React.Component {
render() {
return (
<div>
Hello From React!!
<NewHOC>
<Movie name="Blade Runner"></Movie>
</NewHOC>
</div>
);
}
}
But, the warning I am getting is:
Warning: Functions are not valid as a React child. This may happen if
you return a Component instead of <Component /> from render. Or maybe
you meant to call this function rather than return it.
in NewHOC (created by App)
in div (created by App)
in App
The Movie.js file is:
import React from "react";
export default class Movie extends React.Component{
render() {
return <div>
Hello from Movie {this.props.name}
{this.props.children}</div>
}
}
What am I doing wrong?
I did encounter this error too because I didn't use the correct snytax at routing. This was in my App.js under the <Routes> section:
False:
<Route path="/movies/list" exact element={ MoviesList } />
Correct:
<Route path="/movies/list" exact element={ <MoviesList/> } />
So now the MoviesList is recognized as a component.
You are using it as a regular component, but it's actually a function that returns a component.
Try doing something like this:
const NewComponent = NewHOC(Movie)
And you will use it like this:
<NewComponent someProp="someValue" />
Here is a running example:
const NewHOC = (PassedComponent) => {
return class extends React.Component {
render() {
return (
<div>
<PassedComponent {...this.props} />
</div>
)
}
}
}
const Movie = ({name}) => <div>{name}</div>
const NewComponent = NewHOC(Movie);
function App() {
return (
<div>
<NewComponent name="Kill Bill" />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"/>
So basically NewHOC is just a function that accepts a component and returns a new component that renders the component passed in. We usually use this pattern to enhance components and share logic or data.
You can read about HOCS in the docs and I also recommend reading about the difference between react elements and components
I wrote an article about the different ways and patterns of sharing logic in react.
In my case i forgot to add the () after the function name inside the render function of a react component
public render() {
let ctrl = (
<>
<div className="aaa">
{this.renderView}
</div>
</>
);
return ctrl;
};
private renderView() : JSX.Element {
// some html
};
Changing the render method, as it states in the error message to
<div className="aaa">
{this.renderView()}
</div>
fixed the problem
I encountered this error while following the instructions here: https://reactjs.org/docs/add-react-to-a-website.html
Here is what I had:
ReactDOM.render(Header, headerContainer);
It should be:
ReactDOM.render(React.createElement(Header), headerContainer);
I had this error too. The problem was how to call the function.
Wrong Code:
const Component = () => {
const id = ({match}) => <h2>Test1: {match.params.id}</h2>
return <h1>{id}</h1>;
};
Whereas id is a function, So:
Correct code:
return <h1>{id()}</h1>;
Adding to sagiv's answer, we should create the parent component in such a way that it can consist all children components rather than returning the child components in the way you were trying to return.
Try to intentiate the parent component and pass the props inside it so that all children can use it like below
const NewComponent = NewHOC(Movie);
Here NewHOC is the parent component and all its child are going to use movie as props.
But any way, you guyd6 have solved a problem for new react developers as this might be a problem that can come too and here is where they can find the solution for that.
I was able to resolve this by using my calling my high order component before exporting the class component. My problem was specifically using react-i18next and its withTranslation method, but here was the solution:
export default withTranslation()(Header);
And then I was able to call the class Component as originally I had hoped:
<Header someProp={someValue} />
it also happens when you call a function from jsx directly rather than in an event. like
it will show the error if you write like
<h1>{this.myFunc}<h2>
it will go if you write:
<h1 onClick={this.myFunc}>Hit Me</h1>
I was getting this from webpack lazy loading like this
import Loader from 'some-loader-component';
const WishlistPageComponent = loadable(() => import(/* webpackChunkName: 'WishlistPage' */'../components/WishlistView/WishlistPage'), {
fallback: Loader, // warning
});
render() {
return <WishlistPageComponent />;
}
// changed to this then it's suddenly fine
const WishlistPageComponent = loadable(() => import(/* webpackChunkName: 'WishlistPage' */'../components/WishlistView/WishlistPage'), {
fallback: '', // all good
});
In my case, I was transport class component from parent and use it inside as a prop var, using typescript and Formik, and run well like this:
Parent 1
import Parent2 from './../components/Parent2/parent2'
import Parent3 from './../components/Parent3/parent3'
export default class Parent1 extends React.Component {
render(){
<React.Fragment>
<Parent2 componentToFormik={Parent3} />
</React.Fragment>
}
}
Parent 2
export default class Parent2 extends React.Component{
render(){
const { componentToFormik } = this.props
return(
<Formik
render={(formikProps) => {
return(
<React.fragment>
{(new componentToFormik(formikProps)).render()}
</React.fragment>
)
}}
/>
)
}
}
What would be wrong with doing;
<div className="" key={index}>
{i.title}
</div>
[/*Use IIFE */]
{(function () {
if (child.children && child.children.length !== 0) {
let menu = createMenu(child.children);
console.log("nested menu", menu);
return menu;
}
})()}
In my case I forgot to remove this part '() =>'. Stupid ctrl+c+v mistake.
const Account = () => ({ name }) => {
So it should be like this:
const Account = ({ name }) => {
In my case
<Link key={uuid()} to="#" className="tag">
{post.department_name.toString}
</Link>
changed with
<Link key={uuid()} to="#" className="tag">
{post.department_name.toString()}
</Link>
You should use
const FunctionName = function (){
return (
`<div>
hello world
<div/>
`
)
};
if you use Es6 shorthand function it will give error use regular old javascript function.

Categories

Resources