Search input section - KatyaHorton/Udacity-React-practice GitHub Wiki
Ways of thinking about forms in React:
- bypass components' state completely and get your input field directly to the DOM (typical way)
- React - is made to manage state, so let's pit the
form
state inside of our component
- connect the
input
field to whatever the value of a certain property on put state is - update the UI based on that form data (which we did bind with the property on our state)
If: you want form data to update the UI (besides updating the actual input field itself)
Then: make your form Controlled component
Otherwise: just stick the form data in the DOM to grab it whenever you need it
The reason they are called Controlled Components is because the React is controlling the state of the FORM!
The source of truth for the form state lives inside of a component's state, rather than inside of the DOM.
class NameForm extends React.Component {
state = { email: '' }
handleChange = (event) =>
this.setState({email: event.target.value })
render () {
return (
<form>
<input type='text' value={this.state.email}>
</form>
)}}
We can update the state in the input field only if we update the email property of the component state :)
If we want the input field to change we can create a handleChange method that setState()
to update the email address:
- if input field changes
- we call
handleChange()
method by passing it to theonChange
attribute.
To update the input field we need to update our state (having input state living inside of out component we can update the UI based on that state)
1
Refactor you Stateless Functional component (if you used one) to a class Component.
2
Add query
property to our state (string):
state = {
query: ''
}
3
Wrap all the elements into one div
as React is able to return just one elements
<div>
...
</div>
4
Create a div
with the input field inside, giving it necessary props.
- we want
value
always bethis.state.query
- when the input field changes we want to update our
query
<input
className='search-contacts'
type='text'
placeholder='Search contacts'
//we want ```value``` always be ```this.state.query```
value={this.state.query}
//when the input field changes we want to update our ```query```
onChange={}
/>
5
Create a method that will update query
- will take in a new query as an argument
- update state in based on that new
query
updateQuery = (query) => {
this.setState({ query: query.trim() })
}
6
When ever input field changes it gives us event, which will invoke updateQuery()
passing it the specific value of the input field:
-
event
is just an event object that React is giving us - to get the value of the input field we do
event.target.value
- whenever we type into the input field the
onChange()
function is invoked - that invokes
updateQuery()
passing it the new string inside of an input field - which will then update our state
- which will then update the specific value of the input field
onChange={(event) => this.updateQuery(event.target.value)}
To see that we can:
{JSON.stringify(this.state.query)}
Or to display all the state's properties:
{JSON.stringify(this.state)}
7
Install packages to update state
to filter contacts:
escape-string-regexp
sort-by
npm install --save escape-string-regexp sort-by
- restart application
- import packages
import escapeRegExp from 'escape-string-regexp'
import sortBy from 'sort-by'
8
By now we have been mapping over all of our contacts, but we should only map over those which match a certain pattern:
Using RegExp (regular expressions):
- create new variable
showingContacts
(just contacts which match the specific pattern) - if
this.state.query
truthy (some one has typed smth in our input) - we check which contacts match - else (if nothing is typed) -
showingContacts
will be what ever contacts initially were
render() {
let showingContacts
if (this.state.query) {
...
} else {
showingContacts = this.props.conatcs
}
return(
//the rest of the code
)}
9
If this.state.query
truthy, we:
- create new variable
match
- new instance of RegExp (regular expressions) -
match
- object for matching a specific text in the pattern - we pass RegExr the invocation of
escapeRegExp
, passing it:
- our
query
as a first argument -
i
as a second argument
let showingContacts
if (this.state.query) {
const match = new RegExp(escapeRegExp(this.state.query), 'i')
} else {
showingContacts = this.props.conatcs
}
Walk though: certain characters in RegExp have certain meanings, escapeRegExp(ourQuery) says:
- 'if there are any special characters in our query - escape them, so we will use the, as a string literal, rather then the special Regexp characters'
- 'i' also does smth :)
10
Simple example:
- this will check if 'Tyler' matches
this.state.query
- if it does - returns true
match.text('Tyler')
we say:
-
showingContacts
is the result of callingthis.props.filter
- we filter where
contacts.name
matches our specific RegExp (regular expression) (this.state.query
) -
showingContacts
will only be the contacts which match ourquery
```Javascript
let showingContacts
if (this.state.query) {
const match = new RegExp(escapeRegExp(this.state.query), 'i')
showingContacts = this.props.contacts.filter((contact) =>
match.test(contact.name))
} else {
showingContacts = this.props.conatcs
}
11
Instead of mapping over this.prop.contacts
- we map over shovingContacts
(which is filtered array which match this.state.query
)
<ol className='contact-list'>
{showingContacts.map(contact => {
//rest of the code
12
Sort contacts by name (sortBy
)
showingContacts.sort(sortBy('name'))
render() {
const { contacts, onDeleteContact } = this.props
const { query } = this.state
}
In order not to type each type this.state.query
, we now can type query