Reputation: 55
I am following along with Wes Bos' React for Beginners course, and in the State video, he writes the following code:
import React, { Component } from 'react';
class AddFishForm extends Component {
nameRef = React.createRef();
priceRef = React.createRef();
statusRef = React.createRef();
descRef = React.createRef();
imageRef = React.createRef();
createFish = event => {
event.preventDefault();
const fish = {
nameRef: this.nameRef.value.value,
priceRef: parseFloat(this.priceRef.value.value),
statusRef: this.statusRef.value.value,
descRef: this.descRef.value.value,
imageRef: this.imageRef.value.value,
}
console.log(this); //or console.log(fish)
}
render() {
return (
<form className="fish-edit" onSubmit={this.createFish}>
<input name="name" ref={this.nameRef} type="text" placeholder="Name"/>
<input name="price" ref={this.priceRef} placeholder="Price"/>
<select name="status" ref={this.statusRef}>
<option value="available">Fresh!</option>
<option value="unavailable">Sold Out!</option>
</select>
<textarea name="desc" ref={this.descRef} placeholder="Desc"/>
<input name="image" ref={this.imageRef} type="text" placeholder="Image"/>
<button type="submit">+ Add Fish</button>
</form>
)
}
}
export default AddFishForm;
Whenever I try to click the Add Fish button, it gives an error:
AddFishForm.js:14 Uncaught TypeError: Cannot read property 'value' of undefined
at AddFishForm._this.createFish (AddFishForm.js:14)
at HTMLUnknownElement.callCallback (react-dom.development.js:149)
at Object.invokeGuardedCallbackDev (react-dom.development.js:199)
at invokeGuardedCallback (react-dom.development.js:256)
at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:270)
at executeDispatch (react-dom.development.js:561)
at executeDispatchesInOrder (react-dom.development.js:583)
at executeDispatchesAndRelease (react-dom.development.js:680)
at executeDispatchesAndReleaseTopLevel (react-dom.development.js:688)
at forEachAccumulated (react-dom.development.js:662)
at runEventsInBatch (react-dom.development.js:816)
at runExtractedEventsInBatch (react-dom.development.js:824)
at handleTopLevel (react-dom.development.js:4820)
at batchedUpdates$1 (react-dom.development.js:18932)
at batchedUpdates (react-dom.development.js:2150)
at dispatchEvent (react-dom.development.js:4899)
at interactiveUpdates$1 (react-dom.development.js:18987)
at interactiveUpdates (react-dom.development.js:2169)
at dispatchInteractiveEvent (react-dom.development.js:4876)
There are certain things that I would like to point out in his code. He does not use a constructor (props) function with its associated binding statement, and instead opts for turning createFish into an arrow function. Almost every other question on State I see uses a constructor function. Should I be following suit, or is it okay to use the arrow notation (though it is not standard yet)?
Moreover, in the related StorePicker.js
file, he writes the following code which is supposed to get you from the StorePicker page to the App page via routing. Yet, when I click the Go To Store button in my app, the app only reloads and does not go forth. What should I be doing differently?
Code for StorePicker.js
:
import React, { Fragment } from 'react';
import { getFunName } from '../helpers';
class StorePicker extends React.Component { //OR: ...extends Component (if you import { Component }).
constructor () {
super();
console.log('Create a component!')
this.goToStore = this.goToStore.bind(this);
}
myInput = React.createRef();
goToStore(event) {
//1. Stop the form from submitting.
event.preventDefault();
//2. Get the text from that input. Not going to select using document.querySelector or jQuery here.
/*IMPORTANT!
We need to bind ‘this’ to the StorePicker component so that it remains accessible from a member function.
Either declare a constructor, as shown at the top, or turn goToStore into a function w/ arrow notation.
goToStore = (event) => {}
*/
const storeName = this.myInput.value.value;
//3. Change the page to /store/whatever-they-entered. This uses push state instead of actually moving to a new page.
this.props.history.push(`/store/${storeName}`);
}
render() {
return (
<Fragment>
<h1>StorePicker</h1>
<form className="store-selector">
<h2>Please enter a store.</h2>
<input type="text"
ref={this.myInput}
required placeholder="Store name"
defaultValue={getFunName()}>
</input>
<button type="submit">Visit Store »
</button>
</form>
</Fragment>
)
}
}
export default StorePicker;
And for Router.js
:
import React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import StorePicker from './StorePicker';
import App from './App';
import NotFound from './NotFound';
const Router = () => (
<BrowserRouter>
<Switch>
<Route exact path="/" component={StorePicker} />
<Route path="/store/:storeId" component={App} />
<Route component={NotFound} /> {/*Default catch-all. */}
</Switch>
</BrowserRouter>
);
export default Router;
Upvotes: 1
Views: 1471
Reputation: 202836
When accessing refs, you need to access the current
attribute.
When a ref is passed to an element in
render
, a reference to the node becomes accessible at thecurrent
attribute of the ref.
ref.current.<property>
Code
const fish = {
nameRef: this.nameRef.current.value,
priceRef: parseFloat(this.priceRef.current.value),
statusRef: this.statusRef.current.value,
descRef: this.descRef.current.value,
imageRef: this.imageRef.current.value,
}
He does not use a constructor (props) function with its associated binding statement, and instead opts for turning createFish into an arrow function. Almost every other question on State I see uses a constructor function. Should I be following suit, or is it okay to use the arrow notation (though it is not standard yet)?
AddFishForm
has no state and all the class properties that are functions are arrow functions so they don't need to explicitly have this
bound to them in the constructor. Additionally, you can declare the state class property sans a constructor too.
class Foo extends Component {
state = {...}
...
...code which is supposed to get you from the StorePicker page to the App page via routing. Yet, when I click the Go To Store button in my app, the app only reloads and does not go forth. What should I be doing differently?
The problem here is the button has type="submit"
and is within a form, which when clicked will make the form take default actions, i.e. try to submit the form and reload the page. The this.goToStore
also doesn't appear to be used at all.
<form className="store-selector">
<h2>Please enter a store.</h2>
<input type="text"
ref={this.myInput}
required
placeholder="Store name"
defaultValue={getFunName()}
/>
<button type="submit"> // <-- causes form to submit and take default actions
Visit Store »
</button>
</form>
type="button"
and add an onClick
handler, onClick={this.goToStore}
this.goToStore
to the form's onSubmit
handler <form onSubmit={this.goToStore}>
I'm going to guess goToStore
was intended to be used by the form.
<form
className="store-selector"
onSubmit={this.goToStore}
>
<h2>Please enter a store.</h2>
<input type="text"
ref={this.myInput}
required
placeholder="Store name"
defaultValue={getFunName()}
/>
<button type="submit">
Visit Store »
</button>
</form>
Upvotes: 3