Input in another component, error with target - reactjs

Want to simplify my component, and want the search bar in another component to be more easy to read the code. And I have problems with target. I'm new in React...
export class SearchBar extends React.Component {
constructor(props) {
super(props)
this.handleUserInput = this.handleUserInput.bind(this)
}
render () {
return (
<input
className='SearchBar'
type='text'
placeholder='Search movies...'
onFocus={(e) => e.target.placeholder = ''}
onBlur={(e) => e.target.placeholder = 'Search movies...'}
onChange={()=>this.props.updateInput(this.handleUserInput())}
value={this.props.userInput}
/>
)
}
handleUserInput(e) {
return(e.target.value)
}
}
and the component where is it
<SearchBar value={this.state.userInput} updateInput={this.updateInput}/>
updateInput = (value) => {
this.setState({userInput:value})
}
Uncaught TypeError: Cannot read property 'target' of undefined

You haven't passed the event to your handleUserInput function.
onChange={()=>this.props.updateInput(this.handleUserInput())}
So e in the function is undefined. You need to change it to pass the event
onChange={(e)=>this.props.updateInput(this.handleUserInput(e))}

Related

Moving props data to state to generate forms

I'm planning to add a prefilled form with React. I have the actual data on props. This is what I came up with.
#connect(...)
class Some extends React.Component {
state = {
...this.props.auth.user
}
render() {
// Create a form using the data on state
}
}
It looks not correct since I'm not using a react lifecycle hook here. I would like to ask if there is a better practice to achieve what I'm trying to do.
I am not sure about your architecture,since you are using uncontrolled component here, it is recommended to keep the source of truth at one place.
you can do something like this:
#connect(...)
class Some extends React.Component {
constructor(props) {
super(props);
this.state = {
userName:this.props.auth.user
}
}
handleChange = (event) => {
this.setState({userName: event.target.value});
}
render() {
return(
<div>
<input onChange={this.handleChange} id="some" type="text" value= {this.state.userName}/>
</div>
)
}
}
If you want to use controlled component that is controlled through parent/container. you can manage the values through props and set the props onChange.
So to elaborate on my previous responses you would do something like this to achieve what you want:
#connect(...)
class Some extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '',
}
}
handleChange = (event) => {
this.setState({ value: event.target.value });
}
render() {
return(
<div>
<input onChange={this.handleChange} id="some" type="text" value= {this.state.value|| this.props.value}/>
</div>
)
}
}
While your value is an empty string (in the state), the fields will be populated from your props and as soon as you start typing it will overwrite the prepopulated values with the ones in your state.
Best practices would be to actually have a Component that handles this logic and then passes the props to the form that should be just a dumb presentational component:
class SomeController extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '',
}
}
handleChange = (event) => {
this.setState({ value: event.target.value });
}
return (<Form handleChange={this.handleChange} value={this.state.value} />)
}
And then your form component:
const Form = (props) => (
<form>
<input onChange={props.handleChange} value={props.value} />
</form>
);
Hope this explanation helps.

createRef as prop to createElement

I'm new to React and its processes. How would I add a ref to an element as a prop? So far I am getting null for ref, and undefined for wordRef.
render(){
const {
str = "Functional components cannot leverage on the performance improvements and render optimizations that come with React.",
func = function(){
const words = str.split(" ");
const els = words.map( word => {
const ref = React.createRef();
console.log('ref',ref.current); // null
return React.createElement( "wrd", {attr:"ref-"+ref}, word+" ");
});
return els;
},
wordRef = React.createRef()
} = this.props;
You have some ways in order to create a ref. This is one:
Class components:
class AutoFocusTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
render() {
return (
<input
type="text"
ref={this.textInput} />
);
}
}
If you want to pass this ref as prop, you need to create the child component as class.
Function Components:
function CustomTextInput(props) {
// textInput must be declared here so the ref can refer to it
let textInput = React.createRef();
function handleClick() {
textInput.current.focus();
}
return (
<div>
<input
type="text"
ref={textInput} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
Documentation is useful and you can see how to create a ref properly: https://reactjs.org/docs/refs-and-the-dom.html https://reactjs.org/docs/forwarding-refs.html
However, take care using ref: "Avoid using refs for anything that can be done declaratively."

How to use props of a component out of the component in react with typescript?

As i am new to react i have a question.I have a react component and its properties. And I want to reach one of these properties from the page where i used my component.
type BranchProps = {
SelectedBranch : string
}
class Branch extends React.Component<BranchProps, BranchState> {
constructor(props: BranchProps) {
super(props);
}
render() {
return (
<SelectBox></SelectBox>
)
}
}
export default Branch ;
ParentPage.tsx
import Branch...
class Page extends.... {
ctor..
const test:string = () => {
Branch.SelectedBranch ???
}
}
And i want to get "SelectedBranch" from my ParentPage.
Note: SelectedBranch is changing on change event. Should i make my SelectedBranch a const and export it or what should i do ?
I have created this Input.js child component with different props
const Input = ({ placeholder, label, value, onChangeText, secureTextEntry }) => {
return (
<View >
<Text >{ label }</Text>
<TextInput
secureTextEntry={secureTextEntry}
placeholder={placeholder}
autoCorrect={false}
value={value}
onChangeText={onChangeText}
style={inputStyles}
/>
</View>
);
};
Once I import it to be used on a page, this is how the manipulation of the content is being done. the value is been passed on by simply quoting the specific prop
<Input
secureTextEntry
placeholder={'password'}
label={'Password'}
value={this.state.password}
onChangeText={password => this.setState({ password })}
/>
Here the 'password' is been assigned to the component by using the state of the parent. something like this, you can assign the value as you see fit.
state = { email: '', password: '', error: '', loading: false };
A far better way exist by using the Redux approach. would be advisable to have a look.
Firstly, you should understand the difference between state and props inside a component. Props shouldn't be updated, it's the state's role.
You can't directly access component's props outside of it.
In pure react (without librabry like redux) the right way should be to use callbacks to return the element to the parent.
class Branch extends React.Component<BranchProps, BranchState> {
state = {
'selectedBranch': ''
}
constructor(props: BranchProps) {
super(props);
}
handleOnChange = (e) => {
this.setState({'selectedBranch': e.target.value})
this.props.parentHandleChangeBranch(this.state.selectedBranch);
}
render() {
return (
<SelectBox value={this.state.selectedBranch} onChange="{this.handleOnChange}"></SelectBox>
)
}
}
class Page extends React.Component {
state = {
'branch': null
}
parentHandleChangeBranch = (branch) => {
this.setState({'branch': branch};
}
render () {
<div>
<Branch parentHandleChangeBranch={this.parentHandleChangeBranch} />
</div>
}
}
You can declare a function in the parent component and pass it as prop to the child. Then, call this callback whenever you want inside the child.

Multiple call on same event

I am calling two functions on the same event onChange
but the seconde one dosen't exucute this.updateValue
the value of the input doesn't change
----> but if we remove the first call and we change it to
---> onChange={this.updateValue}: the value on the input changes , it apear that there is a probleme while calling two function at once
import React from 'react'
class SearchBar extends React.Component
{
constructor(props)
{
super(props)
this.state = {input_value : ''}
}
updateValue = (event) =>
{
this.setState({input_value : event.target.value})
}
render()
{
return(
<React.Fragment>
<input
type="text"
value={this.state.input_value}
onChange={() => (this.props.toChild(this.state.input_value,this.updateValue))}
/>
</React.Fragment>
)
}
}
export default SearchBar
One function will be executed on onChange. You can do following
// prop to input in render
onChange={this.updateValue}
// In function
updateValue = (event) =>
{
this.props.toChild(event.target.value)
this.setState({input_value : event.target.value})
}

state props overridden by plugin props

here is the component contains "react-dropzone" plugin below
class Submit extends Component {
constructor(props) {
super(props)
this.props.appState.recipes = JSON.parse(localStorage.getItem("recipes")) || []
}
submitForm() {
debugger //I also get props properly here.
this.props.appState.recipe.name = this.name.value
this.props.history.push('/home')
}
onImageDrop(files) {
debugger //props overridden by Dropzone props :( appState is undefined
this.props.appState.uploadedFileCloudinaryUrl = files[0]
}
render() {
return (
<form onSubmit={() => this.submitForm()}>
<Dropzone
multiple={false}
accept="image/*"
onDrop={this.onImageDrop}>
<p>Drop an image or click to select a file to upload.</p>
</Dropzone>...
)
}
}
export default Submit
I am able to access mobx props in constructor and form on submit method of form(submitForm()) but if i upload a file to Dropzone and check the props content in "onImageDrop()" function I dont recognaize any of properties. Ok for experienced react developers that makes sense but I couldnt understand why its override my own state props and how I can fix it?
Binding issue. Either prebind onImageDrop in constructor (which is preferred way)
constructor(props) {
super(props)
this.submitForm = this.submitForm.bind(this)
this.onImageDrop = this.onImageDrop.bind(this)
this.props.appState.recipes = JSON.parse(localStorage.getItem("recipes")) || []
}
or use arrow function as you did for submitForm
render() {
return (
<form onSubmit={() => this.submitForm()}>
<Dropzone
multiple={false}
accept="image/*"
onDrop={files => this.onImageDrop(files)}>
<p>Drop an image or click to select a file to upload.</p>
</Dropzone>...
)
}

Resources