React, having trouble getting event.key from onKeyPress input event - javascript

I'm having trouble getting event.key from input onKeyPress event, getting this error when i type in the input field - TypeError: Cannot read property 'key' of undefined
The plan is to update parent state when enter is pressed in child input component.
class ToDoList extends Component {
constructor(props) {
super(props);
this.handleKeyPress = this.handleKeyPress.bind(this);
this.state = {
todoArr: [],
};
}
handleKeyPress(e){
console.log(e.key);
}
render(){
return(
<div className="wrapper">
<p className = "title">todos</p>
<InputField testProp={this.handleKeyPress}></InputField>
<div className = "filter-wrap">
<ItemsLeft></ItemsLeft>
<Filter></Filter>
<ClearCompleted></ClearCompleted>
</div>
</div>
)
}
}
class InputField extends Component {
render(){
return(
<input type="text" className="input-field" onKeyPress = {()=> this.props.testProp()}/>
)
}
}

You break the code in the children by doing () => this.props.testProp() if you want this way you should have pass the args like this (e) => this.props.testProp(e)
This is how I would have make it
class ToDoList extends Component {
constructor(props) {
super(props);
this.handleKeyPress = this.handleKeyPress.bind(this);
this.state = {
todoArr: []
};
}
handleKeyPress(e) {
console.log(e.key);
}
render() {
return (
<div className="wrapper">
<p className="title">todos</p>
<InputField testProp={this.handleKeyPress} />
</div>
);
}
}
class InputField extends Component {
render() {
return (
<input
type="text"
className="input-field"
onKeyPress={this.props.testProp}
/>
);
}
}
You can test it here :) https://codesandbox.io/s/6n2jp8yzy3
If you want to see the console you have a button bottom left for open it.
By doing this the callback receive all the args provide by the input, so you don't need to write it by your own.

Related

React - How can I make a component from a function inside a parent component?

New to React - I have a component AddForm that looks like this:
class AddForm extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "",
};
}
handleInput = event => {
this.setState({ name: event.target.value });
};
logValue = () => {
console.log(this.state.name)
return <Display text={this.state.name} />
};
render() {
return (
<div>
<input onChange={this.handleInput}
placeholder="Type Here" type="text" />
<button onClick={this.logValue}
type="submit">Submit</button>
</div>
)
}
}
And when the user clicks on the "Submit" button I want it to display what was in the form. I stored the value in form in this.state.name and my component for displaying the text inside of the form looks like this:
class Display extends React.Component {
render() {
return (
<h1>{this.props.text}</h1>
)
}
}
I know that I the state.name can access the form because I console.logged it. I just want to know why my return statement in the logValue function in the AddForm component isn't creating a new component, Display, and how can I make it work?
The click of the button should result in a state change that the render method then uses to return the Display component - something like:
logValue = () => {
this.setState({ showingName: !this.state.showingName });
}
render() {
return (
<div>
{
this.state.showingName ? <Display text={this.state.name} /> : null
}
<input onChange={this.handleInput}
placeholder="Type Here" type="text" />
<button onClick={this.logValue}
type="submit">Submit</button>
</div>
)
}

Show text in a header tag as it is being typed in an input tag

I just started my journey into the world of React and I love it so far. I finally started to get a grasp of the basics and tried my hand in creating a simple program.
This program simply takes what you are typing in a text box and displays it live in a header tag. I can't seem to figure out why this isn't working. I get the text to appear in the console, but can't figure out why it isn't displaying in my tag. Thank you in advance for the help. Any tips are greatly appreciated.
class Header extends Component{
render(){
return(
<div className = "header">
<h1>Enter Your Name</h1>
</div>
)
}
}
class Input extends Component{
render(){
return(
<div className = "input_container">
<input onChange = {this.props.onChange} className = "input_field" type = "text" placeholder = "Name..."></input>
</div>
)
}
}
class App extends Component {
state = {
inputValue : ""
}
onChange = (e) => {
console.log(e.target.value)
this.setState = ({inputValue: e.target.value});
}
render(){
return(
<div>
<Columns />
<Input
onChange = {this.onChange}/>
<h2>{this.state.inputValue}</h2>
</div>
)
}
}
You have an incorrect syntax for setState. It is a function and not an object
this.setState ({ inputValue: e.target.value });
Your code would be
class App extends Component {
state = {
inputValue: ""
};
onChange = e => {
console.log(e.target.value);
this.setState({ inputValue: e.target.value });
};
render() {
return (
<div>
<Input onChange={this.onChange} />
<h2>{this.state.inputValue}</h2>
</div>
);
}
}

React how to track event through components

I need ideas about how to solve the following problem in Reactjs:
TextEditor -> Toolbar -> PickColorBox
I have component's relationship (parent -> children) as those listed above.
The problem: The component Toolbar has the state showPickColorBox: false that represents whether the component PickColorBox is visible or not. This state is fired by a button's click event. What I need is the button be capable of toggle the boolean value to the correct state, that is, true if the component is visible, false if it's not. But every time the button event is fired the Toolbar's constructor is called and the state showPickColorBox is set to false, even if the PickColorBox component is visible.
My solution: What I need is to track whether the component PickColorBox is visible or not to set the state to the proper value. Reading some codes I saw examples where a class is instantiated in the root component to keep tracking the events. I try to reproduce the example but it didn't work, I wasn't able to passing as props the function inside the class. I'm even not sure whether it's possible to instantiated a class in javascript, so I need some guide here.
Any help on how to solve, any other solution to this is really appreciated!
I change the code bellow to simplify, so typos in the code isn't the problem, unless is about the instantiated class and how to do it. Another thing, just to clarify the code, I'm using Draftjs framework where the component Editor, the states EditorState and onChange are inheritance.
Root component
export default class TextEditor extends React.Component {
constructor(props) {
super(props);
this.state = {
editorState: EditorState.createEmpty(),
};
this.onChange = (editorState) => this.setState({editorState});
this.modalHandler = new ModalHandler();
}
render() {
const {editorState} = this.state;
return (
<div className={'editor-root'} >
<div className={'editor-toolbar'}>
<Toolbar
editorState={editorState}
onChange={this.onChange}
modalHandler={this.modalHandler}
/>
</div>
<div className={'editor-textarea'} >
<Editor
editorState={editorState}
onChange={this.onChange}
/>
</div>
</div>
);
}
}
ModalHandler class
export default class ModalHandler {
let boolShowComponent = false;
toogleShowComponent = (): boolean => {
return !boolShowComponent;
};
}
Toolbar Component
export default class Toolbar extends React.Component {
constructor(props) {
super(props);
this.state = {
showPickColorBox: false
};
}
_onPickColorClick() {
let bool = this.props.modalHandler.toogleShowComponent()
this.setState({
showPickColorBox: bool,
});
}
render() {
return (
<div className={'ToolbarEditor'} >
{this._onPickColorClick.bind(this)}>PickColor</button>
{
this.state.showPickColorBox ?
<PickColorBox
editorState={this.props.editorState}
onChange={this.props.onChange}
/> :
null
}
</div>
);
}
}
Instead of handling Toolbar using showPickColorBox, try to define the same in TextEditor and pass it as props. Now, to update showPickColorBox, define a method in TextEditor and pass it as props.
export default class TextEditor extends React.Component {
constructor(props) {
super(props);
this.state = {
editorState: EditorState.createEmpty(),
//define here
showPickColorBox: false
};
this.onChange = (editorState) => this.setState({editorState});
this.modalHandler = new ModalHandler();
}
//definehere
_onPickColorClick(bool) {
this.setState({
showPickColorBox: bool,
});
}
render() {
const {editorState} = this.state;
return (
<div className={'editor-root'} >
<div className={'editor-toolbar'}>
<Toolbar
editorState={editorState}
onChange={this.onChange}
modalHandler={this.modalHandler}
//pass here
_onPickColorClick={this._onPickColorClick}
/>
</div>
<div className={'editor-textarea'} >
<Editor
editorState={editorState}
onChange={this.onChange}
/>
</div>
</div>
);
}
}
Now call from toolbar:
export default class Toolbar extends React.Component {
constructor(props) {
super(props);
}
_onPickColorClick() {
let bool = this.props.modalHandler.toogleShowComponent();
//use here
this.props._onPickColorClick(bool);
}
render() {
return (
<div className={'ToolbarEditor'} >
{this._onPickColorClick.bind(this)}>PickColor</button>
{
this.state.showPickColorBox ?
<PickColorBox
editorState={this.props.editorState}
onChange={this.props.onChange}
/> :
null
}
</div>
);
}
}
I find the solution to what I was looking for. It happens that I forgot to createing a properly EventHandler class to track the events fired in children component. So here is the solution:
Root component
export default class TextEditor extends React.Component {
constructor(props) {
super(props);
this.state = {
editorState: EditorState.createEmpty(),
};
this.onChange = (editorState) => this.setState({editorState});
/* this name makes more sense instead of modalHandler */
this.toolbarEventHandler = new ToolbarEventHandler();
}
render() {
const {editorState} = this.state;
return (
<div className={'editor-root'} >
<div className={'editor-toolbar'}>
<Toolbar
editorState={editorState}
onChange={this.onChange}
toolbarEventHandler={this.toolbarEventHandler}
/>
</div>
<div className={'editor-textarea'} >
<Editor
editorState={editorState}
onChange={this.onChange}
/>
</div>
</div>
);
}
}
ToolbarEventHandler (ModalHandler)
/*I will change this class to hold more than one event */
/* Probably change to an array */
export default class ToolbarEventHandler {
constructor() {
this.boolShowComponent = false;
}
get boolShowComponent() {
return this.boolShowComponent;
}
set boolShowComponent(bool){
this.boolShowComponent = bool;
}
}
Toolbar
export default class Toolbar extends React.Component {
constructor(props) {
super(props);
this.state = {
showPickColorBox: false
};
}
_onPickColorClick() {
this.props.toolbarEventHandler.boolShowComponent = !this.props.toolbarEventHandler.boolShowComponent;
this.setState({
showComponent: this.props.toolbarEventHandler.boolShowComponent,
});
}
render() {
return (
<div className={'ToolbarEditor'} >
{this._onPickColorClick.bind(this)}>PickColor</button>
{
this.state.showPickColorBox ?
<PickColorBox
editorState={this.props.editorState}
onChange={this.props.onChange}
/> :
null
}
</div>
);
}
}

React : how to get input value from one component to make the ajax call in another component?

I am building a movie search React app using themoviedb.org API. in order to an make ajax call to pull the list of movies I need to get input value as a variable and feed to the url, but not sure how to fetch a value that belongs to another component.
I've made an extensive online search, but they mainly refer to the case when it happens inside the same component, and using ref is discouraged.
So what would be the best way (or at least most common or the simplest way) to fetch the input value variable from one component to pass down to another and attach to the end of url, while:
1) Keeping global space clean
2) Organizing the entire app in 'React way'
3) Keeping components decoupled
?
Would React Router necessary in this case?
import React from 'react';
import './App.css';
import axios from 'axios';
class SearchForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
console.log("state value is " + this.state.value);
var searchValue = this.movieName.value;
console.log("ref value is "+ searchValue)
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input className="movieName" type="text" ref={(input) => { this.movieName = input; }} value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
<h1>{this.state.value}</h1>
</form>
);
}
}
class App extends NameForm{ /* I am extending NameForm to get access to input value, but probably coupling components too tight */
constructor(props) {
super(props);
this.state ={
movie:[]
};
}
componentDidMount() {
let searchInput = "land"; /* This should be from SearchForm's input value */
let sortByPop = "&sort_by=popularity.desc";
let requestUrl = 'https://api.themoviedb.org/3/search/movie?api_key=f8c4016803faf5e7f424abe98a04b8d9&query=' + searchInput + sortByPop;
axios.get(requestUrl).then(response => {
this.setState({movie: response.data.results})
});
}
render() {
let baseImgURL = "https://image.tmdb.org/t/p/w185_and_h278_bestv2";
let posterImgPath = this.state.movie.map(movie => movie.poster_path);
let posterLink = baseImgURL + posterImgPath;
return(
<div className="App">
<Header />
<SearchForm />
<div>
{this.state.movie.map(movie =>
<div className="movieTitle">
<div className="movieCard">
<img className="posterImg" src= {`https://image.tmdb.org/t/p/w185_and_h278_bestv2/${movie.poster_path}`} alt={movie.title} />
<div className="searchFilmTitles" key={movie.id}>{movie.title}</div>
</div>
</div>
)}
</div>
</div>
)
}
}
export default App;
componentDidMount get called only once when your component get attached to the page. So it's not the correct place to call you search API. Instead, you should call it every time when the user clicks 'submit' button. For that, you need to bubble handleSubmit trigger to App component by passing a callback method as a prop to SearchForm component. Also, you don't need to use ref as you already have search text in your state.
SearchForm
class SearchForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
event.preventDefault();
if(this.props.onSubmit && typeof this.props.onSubmit === "function"){
this.props.onSubmit(this.state.value);
}
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input className="movieName" type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
<h1>{this.state.value}</h1>
</form>
);
}
}
App
class App extends React.Component { /* I'm not sure why you extends NameForm and what NameForm does */
constructor(props) {
super(props);
this.state = {
movie:[]
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(value) {
let searchInput = value // you get the value of movieName input here
let sortByPop = "&sort_by=popularity.desc";
let requestUrl = 'https://api.themoviedb.org/3/search/movie?api_key=f8c4016803faf5e7f424abe98a04b8d9&query=' + searchInput + sortByPop;
axios.get(requestUrl).then(response => {
this.setState({movie: response.data.results})
});
}
render() {
let baseImgURL = "https://image.tmdb.org/t/p/w185_and_h278_bestv2";
let posterImgPath = this.state.movie.map(movie => movie.poster_path);
let posterLink = baseImgURL + posterImgPath;
// I'm not sure why use need above code as you don't use it anywhere
return(
<div className="App">
<Header />
<SearchForm onSubmit={this.handleSubmit}/>
<div>
{this.state.movie.map(movie =>
<div className="movieTitle">
<div className="movieCard">
<img className="posterImg" src= {`https://image.tmdb.org/t/p/w185_and_h278_bestv2/${movie.poster_path}`} alt={movie.title} />
<div className="searchFilmTitles" key={movie.id}>{movie.title}</div>
</div>
</div>
)}
</div>
</div>
);
}
}

Pass a trigger function to two components

Here is what i want to do:
render() {
return (
<div>
<Child onTrigger={xxx} />
<button onClick={xxx} />
</div>
)
}
When button is clicked, I want something to happen in Child. How do I do this? It cannot just be a boolean because it should be a trigger and be called multiple times.
You got two options
1º Add a counter to your parent state and increment it everytime you click the button.
2º Add a ref to your child and trigger the function using this.refs.child.yourFunctionHere()
Something like this
class Parent extends React.Component{
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.handleClickWithRef = this.handeClickWithRef.bind(this);
this.state = {
triggerEvent: 0
};
}
handleClick(e) {
this.setState({
triggerEvent: this.state.triggerEvent+1
});
}
handeClickWithRef(e){
this.refs.child.functionA();
}
render() {
return <div>
<Child ref="child" onTrigger={this.state.triggerEvent} />
<button onClick={this.handleClick}>Click to trigger</button>
<button onClick={this.handleClickWithRef}>Click to trigger using ref</button>
</div>;
}
}
class Child extends React.Component{
constructor(props) {
super(props);
}
functionA(){
alert("With ref");
}
componentWillReceiveProps(nextProps){
if(nextProps.onTrigger !== 0 && nextProps.onTrigger !== this.props.onTrigger){
alert("Your event here");
}
}
render(){
return <div>Child Component</div>
}
}
full working example

Categories

Resources