Skip to content

Latest commit

 

History

History
195 lines (143 loc) · 5.19 KB

readme.md

File metadata and controls

195 lines (143 loc) · 5.19 KB

03 State

In this example we introduce a basic React concept: handling State.

In this scenario we provide a default username and let the user update it.

We take as a starting point the example 02 Properties:

Summary steps:

  • Create an App component that holds the state. This state will contain the current username (with default value "defaultUserName"). This App component renders the Hello component. At first we create a simple stateless App component.
  • Update main.tsx file to include our App component.
  • Change App component to a stateful class component to hold the userName state.
  • Create a NameEdit component to let the user change the value of username. This changes the App state using a function from App.
  • Check everything works properly.

Prerequisites

Install Node.js and npm if they are not already installed on your computer.

Verify that you are running at least node v6.x.x and npm 3.x.x by running node -v and npm -v in a terminal/console window. Older versions may produce errors.

Steps to build it

  • Copy the content from 02 Properties and execute npm install.

  • Let's create an App component under a new file named app.tsx (this component will display the Hello component).

./src/app.tsx

import * as React from 'react';
import { HelloComponent } from './hello';

export const App = () => {
  return (
    <HelloComponent userName="John" />
  );
}
  • Let's update main.tsx just to use the App component that we have just created.

./src/main.tsx

  import * as React from 'react';
  import * as ReactDOM from 'react-dom';
+ import { App } from './app';

- import { HelloComponent } from './hello';

  ReactDOM.render(
-    <HelloComponent userName="John" />,
+    <App />,
    document.getElementById('root')
  );
  • Now we can check that things are still working as expected.

    npm start
    
  • It's time to revisit app.tsx. We want to store the user's name and let the user updated it. Let's move this component to a class stateful component and define a state including userName, and pass this value to the Hello component.

./src/app.tsx

import * as React from 'react';
import {HelloComponent} from './hello';

interface Props {
}

interface State {
  userName : string;
}

export class App extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props);

    this.state = {userName: 'defaultUserName'};
  }

  public render() {
    return (
      <HelloComponent userName={this.state.userName} />
    );
  }
}
  • Again, we can do a quick check to test that everything works as expected.

    npm start
    
  • Now it's time to create an NameEdit component. This component lets the user update his username and notifies with a callback to the parent control whenever the value of userName gets updated.

./src/nameEdit.tsx

import * as React from 'react';

interface Props {
  userName : string;
  onChange : (event) => void;
}

export const NameEditComponent = (props : Props) => 
    <>
      <label>Update name:</label>
      <input value={props.userName} 
             onChange={props.onChange}
      />
    </>

Side note: What is this Fragment or <> stuff? A way to create component that has multiple root elements (not a single parent). Available from React 16.2. As an alternative you can type:

  ...
  export const NameEditComponent = (props : Props) => 
    <React.Fragment>
      <label>Update name:</label>
      <input value={props.userName} 
             onChange={props.onChange}
      />
    </React.Fragment>
}
  • In the app.tsx file, let's add a function to replace the state value of userName with the new one.
  import * as React from 'react';
  import {HelloComponent} from './hello';
+ import { NameEditComponent } from './nameEdit';

  interface Props {
  }

  interface State {
    userName : string;
  }

  export class App extends React.Component<Props, State> {
    constructor(props : Props) {
      super(props);

      this.state = {userName: 'defaultUserName'};
    }

+    setUsernameState = (event) => {
+      this.setState({userName: event.target.value});
+    }

    public render() {
      return (
+        <>
          <HelloComponent userName={this.state.userName} />
+          <NameEditComponent userName={this.state.userName} onChange={this.setUsernameState} />
+        </>
      );
    }
  }

Side note: mind the use of the fat arrow function. This avoids losing the context for this in the callback.

Side note 2: this.setState() will change the value of the state at some point in the future. Do not consider it is a synchronous change - it isn't. Writing logic that depends on the username new value being in the state right after calling this.setState() is wrong and might fail. If you need to write code dependent on the new value being in the state, use a callback as second parameter of this.setState(). See this example

  setUserNameState = (newName: string) => {
    this.setState({userName: newName}, this.nameChanged);
  }
  
  nameChanged() {
    /* logic here gets invoked after the new name value in the state is set. */
  }
  • Finally let's test everything works once more.

    npm start