React: why set the document title inside useEffect? - reactjs

I just watched a talk from React Conf 2018. In the video, the speaker shows 2 ways to set the document title. The first is by using the Lifecycle methods (componentDidMount and componentDidUpdate) if we use a class component, the second one is by using the useEffect hook if we use a function component. It looks like that's the recommended way to do it according to answers from this question.
But, I tested the following code and it seems to work just fine to set the document title directly:
import React from 'react';
import ReactDOM from 'react-dom';
function App() {
document.title = 'wow'
return <p>Hello</p>
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
The title changed:
Is there any significance of setting the document title inside useEffect or componentDidMount?
Or is it because it was the only way to set the document title?
Is it okay to set the document title directly like I did in the snippet above?
Update:
It also works with class component:
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
render() {
document.title = 'wow'
return <p>Hello</p>
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
)

The thing is, the render function in React, is considered to be a pure function.
A pure function should not have any side-effects. Eventually updating the document.title means that you are referencing the document directly from render-> which is already considered a side effect.
Render should mainly do one thing -> render some JSX, if there is a need to do some API calls/interact with the document/window etc..., this should be placed in componentDidMount/componentDidUpate for a class Component, OR in a useEffect/useLayoutEfect for a functional component based on hooks.
(useEffect for e.g. is a function that runs asynchronously, and updating the document title from it will not block the rendering, whereas doing it directly will block it.)

Use react-helment which is also widely used in other frameworks with the same name as helmet express ...
Here is the same code:
import {Helmet} from "react-helmet";
class Application extends React.Component {
render () {
return (
<div className="application">
<Helmet>
<meta charSet="utf-8" />
<title>My Title</title>
<link rel="canonical" href="http://example.org/example" />
</Helmet>
...
</div>
);
}
};
Another way to use via props to which is cleaner IMO
...
<Helmet titleTemplate={`%s | ${props.title}`} defaultTitle={constants.defaultTitle} />
You may also use react-helmet-async for more features.

Related

Problem importing stateless function is react

I am very new to react and having a bizarre problem. I have defined a stateless function and when i want to try to import it in my main component it is not loading the function. i am guessing there is a naming convention that i dont know of. npm start does not give any error so I am assuming the code is compiling ok.
My stateless component is below
import React from 'react';
const AppHeader = () => {
return(
<div class="jumbotron text-center">
<h1>Map Search</h1>
<p>Searching map for nearest matches from account</p>
</div>
);
}
export default AppHeader;
below does not work,
import React from 'react';
import './App.css';
import appHeader from './UI/appHeader';
class App extends React.Component {
render(){
return (
<div className="App">
<appHeader/>
</div>
);
}
}
export default App;
VS code lint says
appHeader is declared but its value is never used.
However below does work,
import React from 'react';
import './App.css';
import KKK from './UI/appHeader';
class App extends React.Component {
render(){
return (
<div className="App">
<KKK/>
</div>
);
}
}
export default App;
VS code lint does not show the error anymore also the app is working as expected. As you can see i only changed the name of the appHeader component to KKK. can someone point out what am i doing wrong and may be provide any documentation around this.
You need to capitalize appHeader to be AppHeader in order for React to understand that this is a custom component and not a built in html component. Components that start with lowercase are assumed to be built in HTML elements.
Check out this answer: ReactJS component names must begin with capital letters?
And from the React docs:
User-Defined Components Must Be Capitalized
When an element type starts with a lowercase letter, it refers to a built-in component like or and results in a string 'div' or 'span' passed to React.createElement. Types that start with a capital letter like compile to React.createElement(Foo) and correspond to a component defined or imported in your JavaScript file.
We recommend naming components with a capital letter. If you do have a component that starts with a lowercase letter, assign it to a capitalized variable before using it in JSX.

Why does the page flash all green (in react Rendering chrome addon) when changing anything in redux store

I've created a simple app to test what part of the document gets rerendered when I add items to an array and then use .map in react. To manage the state I use redux. To check what gets rerendered I use the react chrome addon with the option Paint flashing selected.
So I expect that when I dispatch an action from a component that modifies the store, only the components connected to that part of the store would flash green. Instead, the whole screen flashes green followed by every single component that will also flash green.
Seems like anything under <Provider /> will just update on any change inside redux store.
I've already tried PureComponent, managing shouldComponentUpdate, React.memo for the function component.
My index file looks like:
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore } from "redux";
import reducers from "./store/reducers";
import "./index.css";
import App from "./App";
const store = createStore(reducers);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
And my App file:
import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";
import ListComp from "./components/ListComp";
import ListFunc from "./components/ListFunc";
import ButtonMore from "./components/ButtonMore";
export class App extends Component {
shouldComponentUpdate() {
return false;
}
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
<ButtonMore />
<ListComp />
<ListFunc />
</div>
);
}
}
export default App;
ButtonMore will add items to the store when clicked. It has the action connected so it can dispatch it.
ListComp is connected to the list of items in the store and will .map them. In this case, the main purpose was to test the key property and see if only the new items would flash green.
ListFunc Will do the same as the one above but as a function component.
I wanted to drive this test since in the project I work on we are going crazy with performance issues now that the app is huge. We are thinking of moving away from redux but I don't think this option is good at all.
I expected some green flashes just on the new items displayed. But instead the whole screen will always flash when I change anything in the store.
EDIT: Let me add the example that shows the list of items from the store. I expected this to flash only the new items but instead it flashes the whole component:
import React from "react";
import { connect } from "react-redux";
const ListFunc = props => {
return (
<ul className="ListComp">
{props.listItems.map((item, i) => {
return <li key={`itemFunc_${i}`}>{item}</li>;
})}
</ul>
);
};
const mapStateToProps = state => {
return { listItems: state.reducer };
};
export default connect(
mapStateToProps,
null
)(ListFunc);
React-Redux v6 changed the internal implementation in several ways. As part of that, the connect() wrapper components do actually re-render when an action is dispatched, even when your components don't.
For a variety of reasons, we're changing that behavior as part of v7, which is now available as a beta.
update
After looking at the code snippet you've posted: yes, I would still expect the example you've shown to cause both the list items and the list to re-render. I can't say 100% for sure because you haven't shown your reducer, but assuming that one of the list items is updated properly, state.reducer should be a new array reference as well. That will cause ListFunc to re-render.

ReactJS ReactDOM Render for Separate Pages

I am in the process of migrating my pages from html and jquery to using React and I am aware that React Router and Redux are methods to handle routing when building a react application, but for the time being, I was wondering how I can change my setup to be able to render different react components for different pages. At the moment, I am able to render one react component when my index page is loaded, but I thought I could add another ReactDOM.render() beneath it and target a different div id for the component on a different page, but I noticed an error, Invariant Violation: Target container is not a DOM element. Is this related to not using a react router or something else?
here is my index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import ActivityFeed from './components/App/ActivityFeed/ActivityFeed';
import CreateAnnotation from './components/App/Annotation/CreateAnnotation';
ReactDOM.render(<ActivityFeed />, document.getElementById('annotation-card'));
ReactDOM.render(<CreateAnnotation />, document.getElementById('annotation-form'));
Here is <CreateAnnotation/>:
import React from 'react';
//GET /api/activity-feed and set to state
export default class CreateAnnotation extends React.Component {
constructor(props, context) {
super(props, context);
this.state = this.context.data || window.__INITIAL_STATE__ || {
notifications: [],
reportLinks: [],
files: []
}
}
render() {
return (
<div>
<p>test</p>
</div>
)
}
}
Here is the view file:
<!DOCTYPE html>
<head>
{{> app/app-head}}
</head>
<body>
<div id="annotation-form"></div>
{{> general/bundle-js}}
</body>
</html>
{{> general/bundle-js}}:
<script src="/bundle.js"></script>
ReactDOM renders a React element into the DOM in the supplied container
and return a reference to the component (or returns null for stateless
components). If the React element was previously rendered into
container , this will perform an update on it and only mutate the DOM
as necessary to reflect the latest React element.
Technically speaking you cant render more than one React element into the DOM with multiple ReactDOM render function as it would always replace the previous rendered DOM.
See https://reactjs.org/docs/react-dom.html
The right way to do is to create multiple components and import them into App.js file. Then, your index.js file is supposed to import App.js file
as one whole component, with ReactDOM only renders App.js as a single component.

Is there a way to have a React child component displayed as a string, with indentations, returns, and with jsx syntactic sugar?

I'm not sure if I'm explaining what I want right. Here's an example of what I'm trying to do:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class ExampleChild extends Component {
render() {
return (
<p>Hello!!</p>
);
}
}
class ExampleParent extends Component {
render() {
return (
<div>
<ExampleChild />
Here's the ExampleChild code:
{ExampleChild.toString()}
</div>
);
}
}
ReactDOM.render(<ExampleParent />, document.getElementById('root'));
An get the following output to the DOM, with all the correct indentations and spaces of the actual code:
Hello!!
Here's the ExampleChild code:
render() {
return (
< p >Hello!!< /p >
);
}
If the child component is just a stateless, functional component, I can do a .toString(), but it returns it in pure Javascript. It doesn't return it in the JSX format, or with the original returns, and indentations. I would also like to be able to do this with React class components as well. Is there maybe a library that does this?
You could use renderToStaticMarkup from react-dom/server, but only for the resulting HTML markup.
import React from "react";
import { render } from "react-dom";
import { renderToStaticMarkup } from "react-dom/server";
import Hello from "./Hello";
const App = () => (
<div>
<p>Component:</p>
<Hello name="CodeSandbox" />
<p>As HTML:</p>
<pre>{renderToStaticMarkup(<Hello name="CodeSandbox" />)}</pre>
</div>
);
render(<App />, document.getElementById("root"));
Check it out on CodeSandbox.
If you want the whole JSX file, you could import with something like raw-loader and print it inside pres as well.
I ended up using the react syntax highlighting library react-syntax-highlighter
I created an npm script to make copies of my react components and exported them as template literals. Which, then would be displayed via react-syntax-highlighter.
Working example: ryanpaixao.com
I didn't go with raw-loader because I didn't want to eject my create-react-app. (I would've had to in order to configure webpack) Also, it seems that I would've had to set all .js files to be imported as strings (maybe there's a way around that).

Automatic this.props.children in nested components

I'm going through the react-router-tutorial and am on lesson 5 and have a question.
The lesson talks about defining a NavLink component that wraps the Link component, and gives it an activeClassName attribute, which is used as follows:
<NavLink to="/about">About</NavLink>
In the lesson, they define the NavLink component as follows:
// modules/NavLink.js
import React from 'react'
import { Link } from 'react-router'
export default React.createClass({
render() {
return <Link {...this.props} activeClassName="active"/>
}
})
What confuses me is the use of the self closing Link component. No where in the definition of NavLink does it say to put the this.props.children inside of the Link component. I tried it out explicitly as follows:
export default class extends React.Component {
render() {
return (
<Link {...this.props} activeClassName="active">{this.props.children}</Link>
)
}
}
and that also works as expected. My question is why? What allows the self closing Link component in their definition to automatically take the this.props.children of the NavLink and put it inside the Link component?
thats due to the spread attribute on the Link component {...this.props}. This allows all the properties of this.props that includes this.props.children to be passed into the Link component. Here is a reference of that.

Resources