How to change state in a handler - reactjs

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.

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 add input text event handler in react

I was enrolled to a react course in udemy and there is an assigment. There the solution was given but it seems like react library files have been updated so the code needs change for state and evenhandler. Here I am posting the code and the answer I found just in case if anyone needs answer.
import React, { Component } from 'react';
import './App.css';
import UserInput from './UserInput/UserInput';
import UserOutput from './UserOutput/UserOutput';
class App extends Component {
state = {
username: 'jkr'
}
usernameChangedHandler = (event) => {
this.setState({username: event.target.value});
}
render() {
return (
<div className="App">
<ol>
<li>Create TWO new components: UserInput and UserOutput</li>
<li>UserInput should hold an input element, UserOutput two paragraphs</li>
<li>Output multiple UserOutput components in the App component (any paragraph texts of your choice)</li>
<li>Pass a username (of your choice) to UserOutput via props and display it there</li>
<li>Add state to the App component (=> the username) and pass the username to the UserOutput component</li>
<li>Add a method to manipulate the state (=> an event-handler method)</li>
<li>Pass the event-handler method reference to the UserInput component and bind it to the input-change event</li>
<li>Ensure that the new input entered by the user overwrites the old username passed to UserOutput</li>
<li>Add two-way-binding to your input (in UserInput) to also display the starting username</li>
<li>Add styling of your choice to your components/ elements in the components - both with inline styles and stylesheets</li>
</ol>
<UserInput
changed={this.usernameChangedHandler}
currentName={this.state.username} />
<UserOutput userName={this.state.username} />
<UserOutput userName={this.state.username} />
<UserOutput userName="Max" />
</div>
);
}
}
export default App;
Here the code for state and event handler needs modification as following
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
username: 'jkr'
};
this.usernameChangedHandler=this.usernameChangedHandler.bind(this);
}
usernameChangedHandler(event) {
this.setState( { username: event.target.value});
}
This would do
Courtesy: https://reactjs.org/docs/forms.html
https://reactjs.org/docs/hooks-state.html
With the functional components the you should be able to handle hooks to manage state. This is how the code would look like.
import {useState} from "react";
const App = () => {
const [userName, setUserName]=useState("");
userNameChangeEventHandler = (e) => {
setUserName(e.target.value);
}
}

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.

ReactDom.render is not rendering modal component to target 2nd time

i am trying to display modal component when user click on a link.
handleShow fn is called when anchor tag is clicked which calls ReactDom.render to display modal.
handleShow() {
ReactDOM.render(<WaiverModal />, document.getElementById("modal"));
}
i could see modal working for 1st time but 2nd time it doesn't pop up.
This is a case of conditional rendering in react. Instead of making an onClick function to call ReactDOM.render, simply declare a boolean isClicked variable in props, and pass it to that onclick function. Then use the JSX syntax to display that component based on your click.
e.g
isClicked = false;
<a onClick={!isClicked}></a>
<div>
{ this.props.isClicked && <WaiverModal /> }
</div>
I think it's happend because you try to render already the same component second time. How you "unrender" your popup? - this is the question.
So, how you can fix this - create an "modal container" and always render it in some place of page at application start.
import React from "react";
import ReactDOM from "react-dom";
import App from "./component/App";
import ModalContainer from "./component/ModalContainer";
import "./index.css";
ReactDOM.render(<App />, document.getElementById("root"));
ReactDOM.render(<ModalContainer />, document.getElementById("modal"));
Then when you want to render some modal - call function and pass the modal component to it.
showModal(component) {
dispatch({ type: APPEND_MODAL, component }),
}
Then this function will send this component to some storage (redux for example)
import { APPEND_MODAL, HIDE_MODAL } from '../constants/actionTypes';
export default (state = [], action) => {
switch (action.type) {
case APPEND_MODAL:
return [
...state,
action.component
];
case HIDE_MODAL:
return state.slice(0, -1); // Get all exept last
default:
return state;
}
};
and then your "modal container" will pick up this component and render it inside.
class ModalConainer extends React.Component {
render() {
// modals comes from redux - connect ModalContainer to redux store and pass the modals pr
return this.props.modals.map((component, idx) => (
<div key={idx}>{component}</div>
);
}
}
But, sure, you cand find more easy way to do this. It's up to you. Good luck.

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