How to save a component in a Const and render it? - reactjs

I am new in react and basically I want that depending on a condition (I put something simple), a component that I have created called <Button> is rendered or the default <button> of HTML is rendered.
I don't know what I'm doing wrong, in any case it doesn't work, nothing is displayed.
This is my code:
import "./styles.css";
export const Button = ({ children }) => {
return <button>{children}</button>;
};
export default function App() {
let Component = "";
if (1 + 1 == 2) {
Component = <Button />;
} else {
Component = "button";
}
return <Component>I am a button</Component>;
}
This is my live code:
my live code

Change Component = <Button />; to Component = Button;.
Button is a function, <Button /> is an element. When you use <Component>I am a button</Component>, JSX expects Component to be a functional component or class component, not a JSX element.

Related

React Components child component render twice - any way to fix this?

When i click on Update Key button then Child1 component render every time so how can i resolve it.
Note : I have create three component one parent component and two child component when i click on
parent component button to update child component, so child1 component increase to display
multiple time. so please resolved this as soon as possible.
When i click on Update Key button then Child1 component render every time so how can i resolve it.
Note : I have create three component one parent component and two child component when i click on
parent component button to update child component, so child1 component increase to display
multiple time. so please resolved this as soon as possible.
import React, { useState } from "react";
import ReactDOM from "react-dom";
class Child1 extends React.Component {
componentDidMount() {
console.log("mounted");
}
render() {
console.log("rendered");
return <div>Child</div>;
}
}
class Child2 extends React.Component {
componentDidMount() {
console.log("mounted2");
}
render() {
console.log("rendered2");
return <div>Child2</div>;
}
}
class App extends React.Component {
state = {
counter: 0,
counter2: 0
};
onCounter = () => this.setState({ counter: this.state.counter + 1 , counter2: this.state.counter2 + 1 });
render() {
return (
<>
<Child1 key={this.state.counter} />
<Child2 key={this.state.counter2} />
<button onClick={this.onCounter}>Update Key</button>
</>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
my first response, I think I understand the question, I rewrote it using react hooks as it makes it easier to read and requires no configuration changes just make sure your running the latest version of react.
import React, { useState } from "react";
import ReactDOM from "react-dom";
function Child1(props) {
return <h1>Child{props.input}</h1>;
}
function Child2(props) {
return <h1>Child{props.input}</h1>;
}
function App() {
let [counter, setCounter] = useState(0);
let [counter2, setCounter2] = useState(1);
let onCounter = () => {
setCounter(counter+1)
setCounter2(counter2+1)
}
return (
<>
<Child1 input={counter} />
<Child2 input={counter2} />
<button onClick={onCounter}>Update Key</button>
</>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
It is because your Child component, i.e., Child1 and Child2 are using the same value as key. Both counter and counter2 states have the same value and it is creating ambiguity in the ReactDOM. Here, you are not iterating over an array so there's is no need of using the key property. It will work fine. If you've to use the key property, make sure they are unique at the same level.
~Prayag

How to prevent parent component from re-rendering with React (next.js) SSR two-pass rendering?

So I have a SSR app using Next.js. I am using a 3rd party component that utilizes WEB API so it needs to be loaded on the client and not the server. I am doing this with 'two-pass' rendering which I read about here: https://itnext.io/tips-for-server-side-rendering-with-react-e42b1b7acd57
I'm trying to figure out why when 'ssrDone' state changes in the next.js page state the entire <Layout> component unnecessarily re-renders which includes the page's Header, Footer, etc.
I've read about React.memo() as well as leveraging shouldComponentUpdate() but I can't seem to prevent it from re-rendering the <Layout> component.
My console.log message for the <Layout> fires twice but the <ThirdPartyComponent> console message fires once as expected. Is this an issue or is React smart enough to not actually update the DOM so I shouldn't even worry about this. It seems silly to have it re-render my page header and footer for no reason.
In the console, the output is:
Layout rendered
Layout rendered
3rd party component rendered
index.js (next.js page)
import React from "react";
import Layout from "../components/Layout";
import ThirdPartyComponent from "../components/ThirdPartyComponent";
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
ssrDone: false
};
}
componentDidMount() {
this.setState({ ssrDone: true });
}
render() {
return (
<Layout>
{this.state.ssrDone ? <ThirdPartyComponent /> : <div> ...loading</div>}
</Layout>
);
}
}
export default Home;
ThirdPartyComponent.jsx
import React from "react";
export default function ThirdPartyComponent() {
console.log("3rd party component rendered");
return <div>3rd Party Component</div>;
}
Layout.jsx
import React from "react";
export default function Layout({ children }) {
return (
<div>
{console.log("Layout rendered")}
NavBar here
<div>Header</div>
{children}
<div>Footer</div>
</div>
);
}
What you could do, is define a new <ClientSideOnlyRenderer /> component, that would look like this:
const ClientSideOnlyRenderer = memo(function ClientSideOnlyRenderer({
initialSsrDone = false,
renderDone,
renderLoading,
}) {
const [ssrDone, setSsrDone] = useState(initialSsrDone);
useEffect(
function afterMount() {
setSsrDone(true);
},
[],
);
if (!ssrDone) {
return renderLoading();
}
return renderDone();
});
And you could use it like this:
class Home extends React.Component {
static async getInitialProps({ req }) {
return {
isServer: !!req,
};
};
renderDone() {
return (
<ThirdPartyComponent />
);
}
renderLoading() {
return (<div>Loading...</div>);
}
render() {
const { isServer } = this.props;
return (
<Layout>
<ClientSideOnlyRenderer
initialSsrDone={!isServer}
renderDone={this.renderDone}
renderLoading={this.renderLoading}
/>
</Layout>
);
}
}
This way, only the ClientSideOnlyRenderer component gets re-rendered after initial mount. đź‘Ť
The Layout component re-renders because its children prop changed. First it was <div> ...loading</div> (when ssrDone = false) then <ThirdPartyComponent /> (when ssrDone = true)
I had a similar issue recently, what you can do is to use redux to store the state that is causing the re-render of the component.
Then with useSelector and shallowEqual you can use it and change its value without having to re-render the component.
Here is an example
import styles from "./HamburgerButton.module.css";
import { useSelector, shallowEqual } from "react-redux";
const selectLayouts = (state) => state.allLayouts.layouts[1];
export default function HamburgerButton({ toggleNav }) {
let state = useSelector(selectLayouts, shallowEqual);
let navIsActive = state.active;
console.log("navIsActive", navIsActive); // true or false
const getBtnStyle = () => {
if (navIsActive) return styles["hamBtn-active"];
else return styles["hamBtn"];
};
return (
<div
id={styles["hamBtn"]}
className={getBtnStyle()}
onClick={toggleNav}
>
<div className={styles["stick"]}></div>
</div>
);
}
This is an animated button component that toggles a sidebar, all wrapped inside a header component (parent)
Before i was storing the sidebar state in the header, and on its change all the header has to re-render causing problems in the button animation.
Instead i needed all my header, the button state and the sidebar to stay persistent during the navigation, and to be able to interact with them without any re-render.
I guess now the state is not in the component anymore but "above" it, so next doesn't start a re-render. (i can be wrong about this part but it looks like it)
Note that toggleNav is defined in header and passed as prop because i needed to use it in other components as well. Here is what it looks like:
const toggleNav = () => {
dispatch(toggleLayout({ id: "nav", fn: "toggle" }));
}; //toggleLayout is my redux action
I'm using an id and fn because all my layouts are stored inside an array in redux, but you can use any logic or solution for this part.

React - What is meant by 'Do not use HOC’s in the render method of a component. Access the HOC outside the component definition.'?

I am learning HOCs and keep reading the above quote, but I do not understand what it means. If my HOC adds a method to my consuming component, can I use that method in the render method like so? If not how would I do what I am trying to do here:
import React, { Component } from 'react';
import { withMyHOC } from '../with_my_component'
class MyComponent extends Component {
constructor(props) {
super(props);
}
render() {
const { methodFromHOC }= this.props;
const result = methodFromHOC(someArgument);
return (
<div >
{result}
</div>
)
}
}
export default withMyHOC(MyComponent );
When you say, do not use HOC within the render method, it means that you shouldn't create an instance of the component wrapped by HOC within the render method of another component. For example, if you have a App Component which uses MyComponent, it shouldn't be like below
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props) {
super(props);
}
render() {
const { methodFromHOC }= this.props;
const result = methodFromHOC(someArgument);
return (
<div >
{result}
</div>
)
}
}
export default MyComponent;
import { withMyHOC } from '../with_my_component'
export default class App extends React.Component {
render() {
const Wrap = withMyHOC(MyComponent);
return (
<div>
{/* Other Code */}
<Wrap />
</div>
)
}
}
Why you shouldn't use it like above is because everytime render method is called a new instance of the MyComponent is created wrapped by HOC called Wrap and hence everytime it be be mounted again instead of going by the natural lifecycle or React.
However if your HOC passes a function as props, you can use it within the render as long as it doens't cause a re-render again otherwise it will lead to a infinite loop.
Also its better to memoize functions which are called in render directly to avoid computation again and again
CodeSandbox Demo
A High Order Component is a function which returns a Component, not jsx. When wrapping a component with an hoc, you're not changing the returned value of your component, you're changing the signature itself. Consider the following hoc
const withFoo = Component => props =>{
return <Component {...props} foo='foo' />
}
withFoo is a function which takes a Component (not jsx) as argument and returns a component. You don't need to call an hoc from render because the values it injects are already inside props of the wrapped component.
An hoc tells how a wrapped component will look like, changes it's definition so the only place to use it is in the component definition itself. Calling an hoc inside render creates a new instance of that component on each render. It's the equivalent of
const Component = () =>{
const ChildComponent = () =>{
return <span> Child </span>
}
return <ChildComponent /> //You're declaring again on each render
}
Use your high order components like this
const Component = ({ foo }) => <div>{ foo }</div>
export default withFoo(Component)
Or
const Component = withFoo(({ foo }) => <div>{ foo }</div>)

How to change state in a handler

I have a React project and it's using Recompose. Let's say I have a Form, and I supply a 'withHandler' to be used for ..
How can I also change the state of the React component when the Form is submitted?
So let's say the form is submitted with a button, and we have a onClick attribute on the button.
It's a very simple example but hopefully shows you how you would update state with the onClick. Remember, this an attribute that can be applied on HTML elements. You can read about it the onClick attribute here.
import React, { Component } from 'react';
import React from "react";
import { render } from "react-dom";
import Component from "react-component-component";
class Button extends Component {
state = {
counter: 0
};
handleButtonClick = () => {
this.setState({
counter: this.state.counter + 1
});
};
getButton = () => {
const { text } = this.props;
return (
<button
onClick={this.handleButtonClick}
>
{text}
{this.state.counter}
</button>
);
};
render() {
return <div>{this.getButton()}</div>;
}
}
render(
<Button text="press me to increase counter: " />,
document.getElementById("root")
);
The following can be seen here: https://codesandbox.io/s/ly11qv0vr7
There is also a very good example of react documentation regarding handling events. You can read about handling events in react here. I believe the above link will provide you all the information needed to be able to handle a form being submitted.

Passing Props between components

I am creating a bit of a playground to learn react and I've hit a road block with passing props between components. I essentially have two components, 1 that is the base component and then another that renders it out on the page with some extras (which i've removed for simplicity sake). I essentially want to be able to reuse the items in other places.
I'd like to be able to, when rendering the component in the example specify if it is type=submit if nothing specified default to type=button.
I'm clearly missing the point here because I get the error Cannot read property 'props' of undefined with the below code. Any help would be appreciated
Button Component
import React, {PropTypes} from 'react';
import './button_component.scss';
const propTypes = {
type: PropTypes.string
}
const ButtonComponent = () => {
return <button type={this.props.type}>button</button>
}
ButtonComponent.propTypes = propTypes;
export default ButtonComponent;
Then I have a component that outputs my item
import React from 'react';
import ButtonComponent from './button_component';
import Example from './example'
export default () =>
<Example>
<ButtonComponent type='button' />
</Example>;
ButtonComponent is a functional component. Hence, you can not use this.props in its code.
You should introduce props argument
const ButtonComponent = (props) => {
return <button type={props.type}>button</button>
}
Object destructuring and defaultProps can help you make your code simpler
const ButtonComponent = ({ type }) => {
return <button type={type}>button</button>
}
ButtonComponent.defaultProps = {
type: 'button'
}
then you can simply put <ButtonComponent /> to render a button.

Resources