Differences in creating/using components in React - javascript

I've been learning React following a couple different tutorials and I noticed some differences in the creation of components.
In one App.js file a component is made as follows:
import React, { Component } from "react";
import { BrowserRouter as Router, Route } from 'react-router-dom';
import ListEmployees from "./components/listEmployees";
class App extends Component {
render() {
return (
<Router>
<div className="App">
<Route exact path="/" component={ListEmployees} />
</div>
</Router>
)
}
}
export default App;
And in another project the component is instead created like so:
import React, { Fragment, Component } from "react";
import { BrowserRouter as Router, Route } from 'react-router-dom';
import './App.css';
import ListEmployees from "./components/listEmployees";
import displayNavbar from "./components/navbar";
const App = () => {
return (
<Fragment>
<div className="container">
<ListEmployees />
</div>
</Fragment>
)
}
export default App;
What is the difference between these two components and are there advantages to using one way over the other?

Your first example is a class component. This used to be the only way to build React components (pre v16.8). In comparison to functional components, which is what your second example is, they can be more confusing. Developers, and Facebook, wanted easier ways to create React components.
Enter functional components, which utilize React Hooks (https://reactjs.org/docs/hooks-intro.html).
In my experience, functional components are easier to write, debug, and maintain. There are a few more complex problems that Hooks solve, which you can read about in the link I previously posted.
It's largely a matter of preference, but the vast majority of people I see use functional components.

The first example you gave is a class component. The second is a functional component.
Class components are the original way to make components. According to Facebook, functional components are the future.
Official Docs:
https://reactjs.org/docs/components-and-props.html

Related

Will Redux inside a React component from npm conflict with the container's Redux?

I want to encapsulate a npm React component, and I want to use Redux to manage state inside this React component.
When another React project imports my component, will this React project's redux instance have conflict with my React component?
Here is the example:
The component will look like:
// File Explorer Component, will be build into a npm package
import React from "react";
import { Provider } from "react-redux";
import { store } from "./store";
function FileExplorer({source}) {
<Provider store={store}>
{/* there will be complicated business logic inside */}
</Provider>;
}
Then, I create another React project, and import the FileExplorer component from npm
index.jsx
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { store } from "./store"; // this store is a different one
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<Provider store={store}>
<App />
</Provider>
</StrictMode>,
rootElement
);
App.jsx
import FileExplorer from 'file-explorer';
export default function App() {
return (
<div className="App">
<FileExplorer source={'https://example.com/data/v1/files'} />
</div>
);
}
A couple thoughts here.
The first is yes, this would absolutely cause a clash if the components nested inside of <FileExplorer> also need to access the app-wide Redux store, because by default all Redux-connected React components access the same store instance via React context.
It is technically possible to pass an alternate context to <Provider> and create customized versions of useSelector/useDispatch that read the store from that context:
https://react-redux.js.org/using-react-redux/accessing-store#providing-custom-context
although it looks like we don't actually document the API for creating the context-customized versions of the selector hooks atm.
React-Redux should be exporting createSelectorHook and createDispatchHooks functions, which you can see in use here:
https://github.com/reduxjs/react-redux/blob/v7.2.6/src/hooks/useSelector.js#L166
Should be used as:
const useCustomContextSelector = createSelectorHook(MyCustomContext);
All that said: I'd still lean towards tracking the state in a useReducer hook as the default rather than an actual separate Redux store.
Note that you can use RTK's createSlice to create a reducer specifically for use in a useReducer hook - reducer functions are just functions and work fine in either place.

ReactJS - Accessing data on Different Pages

I am trying to access data gathered from a user on one page and use it on another page. I have tried following these articles:
https://travishorn.com/passing-data-between-classes-components-in-react-4f8fea489f80
https://medium.com/#ruthmpardee/passing-data-between-react-components-103ad82ebd17
https://codeburst.io/react-js-pass-data-from-components-8965d7892ca2
I have not been able to get it to work. this.props.{variableName}keeps returning as undefined. My code is as follows.
The following is the Home Page:
import React, { Component } from 'react';
import {Button} from 'reactstrap';
import { browserHistory } from 'react-router';
class HomeScreen extends Component {
constructor(props) {
super(props);
this.state = {
working: "",
};
}
WorkReqNav(){
this.setState=({working: "WORKING"});
browserHistory.push("/WorkReq");
}
render() {
return (
<div>
<Button size="lg" onClick={this.WorkReqNav.bind(this)} type='button'>HIT IT!</Button>
</div>
);
}
}
export default HomeScreen;
The following is the workReq screen:
import React, { Component } from 'react';
import {Button} from 'reactstrap';
class WorkReq extends Component {
constructor(props) {
super(props);
}
workCheck(){
var working = this.props.working;
alert(working);
}
render() {
return (
<div>
<Button size="lg" onClick={this.workCheck.bind(this)} type='button'>HIT IT!</Button>
</div>
);
}
}
export default WorkReq;
If you need anything more, please let me know. i am really new to React and this is my first time attempting anything like this.
welcome to the React world. I bet you'll love it when you gradually get familiar with cool stuff that you can do with React. Just be patient and keep practicing.
So the first suggestion I would make is that, like any other javascript environment, React also evolves very quickly. So although basic principles are the same, when you follow a new article on one hand, on the other hand you can check if the libraries or methodologies that are demonstrated are up to date.
Fasten your belts and let's do a quick review based on your question and libraries that I see you used in your example.
In terms of router, I see that you directly export things from react-router
When we check the npm page (https://www.npmjs.com/package/react-router) of react-router they make the following suggestion
If you are writing an application that will run in the browser, you
should instead install react-router-dom
Which is the following package https://www.npmjs.com/package/react-router-dom
You can get more details and find more tutorials in order to improve your skills by checking their official page https://reacttraining.com/react-router/web/guides/philosophy
Let's take a look at the code snippet sasha romanov provided that's based on react-router-dom syntax
with react-router-dom when you define a route with following syntax
<Route path="/" component={HomePage} exact />
react-router-dom automatically passes match, location, and history props to HomePage component. So when you console.log() these props, you should be able to display somethings on your console. And once you have access to history props, instead of browserHistory, you can use this.props.history.push("/some-route") for redirections.
Let's take a look at the part related to withRouter. In the example above, we could use history because HomePage component was passed directly to the Router component that we extract from react-router-dom. However, in real life, there might be cases in which you want to use history props in a component that's not passed to the Router but let's say just a reusable button component. For these cases, react-router-dom provides a Higher Order Component called withRouter
A Higher Order Component is (from React's official documentation)
https://reactjs.org/docs/higher-order-components.html
Concretely, a higher-order component is a function that takes a
component and returns a new component.
So basically, whenever you wrap any component with withRouter such as export default withRouter(MyWrappedReusableComponent), in your reusable component, you will have access to the props history, location, pathname
That said, my first impression regarding to your problem does not seem to be related to router logic but rather exchanging data between components.
In your original question, you mentioned that
I am trying to access data gathered from a user on one page and use it on another page
There are a couple of cases/ways to approach this issue
1) If these two components are completely irrelevant, you can use state management system such as Redux, mobx or you can use React's context API https://reactjs.org/docs/context.html. HOWEVER, since you are new to React, I would suggest not tackle with these right know till you are comfortable with the basic flow. Because at some point trying to implement a flow with a lot of libraries etc. is quite overwhelming. Believe me, I tried when I was also new to React and I was really close to break my computer after opening my 100th browser tab to look for another method from another library
2) You can implement a simple parent-child relationship to pass data between components. Let me explain what I mean by using references from your code snippet.
I believe you want to update working which is a state in your HomeScreen and you want to pass and use this updated value in your WorkReq component.
If we ignore all the routing logic and decide to go without routes, what you need to do is the following
import React, { Component } from 'react';
import {Button} from 'reactstrap';
import { browserHistory } from 'react-router';
import WorkReqComponent from 'path/to/WorkReqDirectory';
class HomeScreen extends Component {
constructor(props) {
super(props);
this.state = {
working: "WORKING",
};
}
render() {
return (
<div>
<WorkReqComponent working={this.state.working} />
</div>
);
}
}
By this way, when you log this.props.working; in your WorkReqComponent you should be able to display the data that you passed. You can refer to this as passing data from parent to child.
I checked the articles you listed. They also seem to explain data transfer between parent to child, child to parent or between siblings.
In your case, what you really need to implement can be categorized as between siblings
I prepared a sample for you with react-router-dom to demonstrate one possible structure which might yield your expected outcome.
https://codesandbox.io/s/ojp2y0xxo6
In this example, the state is defined inside of the parent component called App. Also state update logic is also defined inside of the parent component. HomeScreen and WorkReq components are the children of App thus they are siblings. So, in order to transfer data between siblings, one of them was given the task of updating parent's state via passing state update logic to this component. The other one has the task of displaying parent's state's value.
At this point, since you are new and in order not to overwhelm yourself, you can experiment with parent-child-sibling data transfer topic. Once you are getting comfortable with the implementation and the logic, you can gradually start taking a look at React's context api and Redux/mobx.
Let me know if you have any questions regarding to the sample I provided
You can use react-router-dom lib and from seeing your code i think in parent component (app.js) you defined route for each child component you'd like to access
like this example here:
import { BrowserRouter, Route, Switch } from 'react-router-dom';
<BrowserRouter>
<div>
<Switch>
<Route path="/" component={HomePage} exact />
<Route path="/homescreen" component={HomeScreen} />
<Route path="/workreq" render={(props) => <WorkReq {...props} />} /> // here you can pass the props by calling render
<Route component={NoMatch} />
</Switch>
</div>
</BrowserRouter>
and then if you want to change route you can just call this.props.history.push('/workreq')
and if you didn't include route for the component in <BrowserRouter />
in the component that it's not included you can import withRouter and export like this withRouter(HomeScreen) and now you can access router props
if this isn't the answer you are looking please inform me to update my answer, i hope this can help

Why HOC are applied during exporting of component in place of importing it

My basic understading is that HOC like connect (for connecting with redux store) and other HOC's are applied to a component while exporting it.
Like this
import React, { Component } from 'react';
import './App.css';
import myHoc from './myHoc/index';
class App extends Component {
render() {
return (
<div className="App">
</div>);
}
}
export default myHoc({})(App);
Where as a better thing would be to apply HOC during import as it would make it easier to make reusable component. The same component can pick up props from store or from props and that would be the responsibility of the parent component to check what to give which HOC to apply on the component.
I know we can use container components which takes the component and render children but that just adds code in the JSX (wont look good if there are many container components)
though we can do it like this
import React, { Component } from 'react';
import './App.css';
import myHoc from './myHoc/index';
import AppChild from './AppChild';
const NewAppChild = myHoc({}, ()=> {
})(AppChild);
class App extends Component {
state = {
count: 1,
};
reRender = () => {
this.setState({count: this.state.count + 1});
};
render() {
return (
<div className="App">
<NewAppChild handleClick={this.reRender} count={this.state.count}/>
</div>
);
}
}
export default App;
What my question is that, is there something better that can handle this kind of situations where I want to apply my HOC on import that is each many container components can import it and they can apply different HOCs depending on the needs.
There is no single concrete reason for this design choice - as you have already seen you can invoke your HOC wherever you use the component - but I see at least 1 advantage: configuration & component reuse.
In your example, myHoc takes no parameters or configuration so this doesn't necessarily apply, but imagine instead that you are invoking connect from redux.
In most use cases, connect accepts 2 configuration functions -
mapStateToProps & mapDispatchToProps - that define the behaviour. If you define those within MyComponent then any consuming component can import MyComponent from 'MyComponent' and start using it.
If you instead rely on the parent component to call connect() then you are forcing every consumer to re-implement the configuration of connect as well. That may mean many instances of duplicated configuration and adds to the complexity for consuming components.
That being said, there are certainly cases where you might want this behaviour - for example, if you wanted to connect the same component to different state definitions. Ultimately you need to pick the best pattern to support what you need from the component.

React routes not automatically updating when nested under 'smart' Redux container components

I'm trying to create an Electron app using React, React-router and Redux. What I'm finding is that my routing logic works absolutely fine when I'm nesting the switch/route logic under a purely presentational component (Page), but that I'm forced to refresh the page to see navigational changes if nested under a 'smart' container component.
Near the top of my React component hierarchy (right beneath HashRouter) I have a Page:
export default function Page (props) {
return (
<div className={`${styles.page}`}>
<SideBar/>
<DetailPane>{props.children}</DetailPane>
</div>
);
}
Here, DetailPane and SideBar are both container components wrapped around presentational components of the same name.
At startup (and during hot reloads), I create my React hierarchy using this function:
export default () => (
<Router>
<Page>
<Switch>
<Route exact path='/txDefinitions/:definitionName/:fieldName' component={FieldPage}/>
<Route exact path='/txDefinitions/:definitionName?' component={DefinitionPage}/>
<Route exact path='/rxDefinitions/:definitionName?' component={DefinitionPage}/>
<Route exact path='/'/>
<Route component={Route404}/>
</Switch>
</Page>
</Router>
This means that <Switch>...</Switch> gets nested underneath <DetailPane>.
If I try to navigate around my app (clicking links in the side bar), I won't actually see the detail pane render the new component until I force-reload the Electron app.
However, I find that routing works as expected if I omit DetailPane from Page:
export default function Page (props) {
return (
<div className={`${styles.page}`}>
<SideBar/>
{props.children}
</div>
);
}
Here is my React hierarchy without DetailPane (works fine):
Here is my React hierarchy with DetailPane (does not work right):
(Apologies for using images but I'm not sure if there's a way to copy from React devtools into clipboard - appears larger if opened in a new tab).
As I was writing this question, I realised this wouldn't be a huge issue for me because earlier refactoring had made the 'smart' version of DetailPane apparently obsolete. Using the purely presentational version of DetailPane
instead resolves this issue:
import * as React from 'react';
//import {DetailPane} from '../../containers'; // Smart/Redux
import {DetailPane} from '../../components'; // Dumb/presentational
import {SideBar} from '../../containers/';
const styles = require('./Page.scss');
export default function Page (props) {
return (
<div className={`${styles.page}`}>
<SideBar/>
<DetailPane>{props.children}</DetailPane>
</div>
);
}
However, I'm still curious why this doesn't work for the container component version. For reference, this is the container component version of DetailPane:
import {connect} from 'react-redux';
import {DetailPane} from '../../components';
// TODO: delete this container?
function mapStateToProps (state): {} {
return {};
}
function mapDispatchToProps (dispatch) {
// TODO.
return {};
}
export default connect(mapStateToProps, mapDispatchToProps)(DetailPane);
The connect HOC implements shouldComponentUpdate logic so if the props don't change, the component doesn't update.
To prevent this from occurring, and have the component always render, you can override the pure option in the connect call.
export default connect(mapStateToProps, mapDispatchToProps, undefined, { pure: false })(DetailPane);
See the react-redux API docs for more details.

ReactJS - Can't import component

I'm brand new to ReactJS. I'm developing a little single page app and I'm just trying to create my components to import within my main component.
TestComponent.jsx
import React from 'react'
export class TestComponent extends React.Component {
render() {
return (
<div className="content">Test Component</div>
)
}
}
Inside my main.jsx I've imported this component calling
import TestComponent from './components/TestComponent.jsx'
Then I've tried to call my component for a specific route:
render(
(<Router history={history}>
<Route path="/" component={NavMenu}>
<IndexRoute component={Index}/>
<Route path="test" component={TestComponent}></Route>
</Route>
</Router>),
document.getElementById('main')
)
I've not got any errors from the console, but I don't see my component. What am I doing wrong?
The import syntax without curly braces is for importing default exports, not for importing named exports.
Make your component the default export:
TestComponent.jsx
import React from 'react'
export default class TestComponent extends React.Component {
render() {
return (
<div className="content">Test Component</div>
)
}
}
Alternatively you should be able to import it as it is with the following import statement:
import { TestComponent } from './components/TestComponent.jsx'
You might want to read up on ES6 modules (e.g. in Exploring ES6) if you want to use ES6 in your React code.
Make sure to include semicolons after the import statements too. you might get away with a browser (or environment like Node) reading the code in some cases, but the import function runs right into your export in this code.
.js should have the first letter capital. Else import will not take place.

Categories

Resources