Creating onClick event for datalist option in React - reactjs

I've made a Twitch API widget which you can see here: https://twitch-react-drhectapus.herokuapp.com/
At the moment, any time you search for something, there will be a list of suggestions. I'd like to make it so that when you click on one of the datalist options it will search for that user, rather than having to click on the 'Search' button. Basically the same search function as google has.
How do I go about implementing this?
Code:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchUser, fetchSuggestions } from '../actions/index';
class SearchBar extends Component {
constructor(props) {
super(props);
this.state = {
term: ''
};
this.onInputChange = this.onInputChange.bind(this);
this.onFormSubmit = this.onFormSubmit.bind(this);
}
onInputChange(event) {
this.setState({
term: event.target.value
});
setTimeout( this.props.fetchSuggestions(event.target.value), 300);
}
renderSuggestions(sug, i) {
return (
<option key={i} value={sug.display_name} />
)
}
onFormSubmit(event) {
event.preventDefault();
this.props.fetchUser(this.state.term);
this.setState({
term: ''
});
}
render() {
const { error, suggestions } = this.props;
return (
<form
className='input-group'
onSubmit={this.onFormSubmit}>
<input
className='form-control'
placeholder='Search for a Twitch user'
value={this.state.term}
onChange={this.onInputChange}
list='suggestions' />
<span className='input-group-btn'>
<button className='btn btn-primary'>
Search
</button>
</span>
<datalist id='suggestions'>
{suggestions.map(this.renderSuggestions)}
</datalist>
</form>
// {/* {error && <div className='alert alert-danger'>{error}</div>} */}
)
}
}
function mapStateToProps({ error, suggestions }) {
return { error, suggestions };
}
export default connect(mapStateToProps, { fetchUser, fetchSuggestions })(SearchBar);

Related

Cannot get input value on submit function

I am trying to display input value on submit. But it seems to be not working. I don't have any errors but nothing being rendered. What is wrong with the code?
import React from 'react';
import { Component } from 'react';
class App extends Component {
constructor (props) {
super(props)
this.state = {
city : ""
}
}
handleSubmit = (event)=> {
event.preventDefault();
this.setState ({
city : event.target.value
})
}
render () {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input type = "text" city = "city_name" />
<button type="submit">Get Weather</button>
{this.state.city}
</form>
</div>
)
}
}
export default App;
<form
onSubmit={e=>{
e.preventDefault()
console.log(e.target[0].value)
}}>
<input type="text"/>
<button type="submit">dg</button>
</form>
that works for me very well
Remember onSubmit target:
Indicates where to display the response after submitting the form. So you can get inner elements (and their corresponding values) like normal javascript code.
const city = event.target.querySelector('input').value
handleSubmit = (event) => {
event.preventDefault();
this.setState ({ city })
}
I guess you want it to get work like below. But this is not the only way to get it done.
import React from "react";
import { Component } from "react";
class App extends Component {
constructor(props) {
super(props);
this.state = {
city: ""
};
}
handleSubmit = (event) => {
const formData = new FormData(event.currentTarget);
event.preventDefault();
let formDataJson = {};
for (let [key, value] of formData.entries()) {
formDataJson[key] = value;
}
this.setState(formDataJson);
};
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input type="text" name="city" />
<button type="submit">Get Weather</button>
{this.state.city}
</form>
</div>
);
}
}
export default App;
Code sandbox => https://codesandbox.io/s/eager-oskar-dbhhu?file=/src/App.js

React checkbox local storage

I am building a To-Do List web app with React as my first project.
I want to implement local storage which works fine only that,I am unable to handle check and uncheck of the checkbox prefectly.
Here is a link to the deployed website so you can understand the problem I am having.
https://rapture-todo.netlify.app/
When you add a todo, and mark it complete.
on reload, the checkbox of the todo is unchecked but the todo is marked complete.
Here is my source code[github link- https://github.com/coolpythoncodes/React-ToDo-List].
For App.js
import React, { Component } from 'react';
import Header from './component/Header';
import Info from './component/Info';
import AddToDo from './component/AddToDo';
import TodoListItem from './component/TodoListItem';
import './sass/main.scss';
class App extends Component{
constructor(props){
super(props);
this.state= {
value: '',
list: [],
show: true,
};
this.handleChange= this.handleChange.bind(this);
this.handleSubmit= this.handleSubmit.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.deleteTask = this.deleteTask.bind(this);
}
componentDidMount() {
const list = window.localStorage.getItem('userTodo') ? JSON.parse(localStorage.getItem('userTodo')) : [];
this.setState({ list })
}
handleChange(e) {
this.setState({value:e.target.value})
}
// Handle submission of user todo item
handleSubmit(e) {
e.preventDefault();
const newTask = {
id: Date.now(),
userTodo: this.state.value,
isCompleted: false,
checked: false,
}
// Validate form so user doesn't add an empty to do
if (this.state.value.length > 0) {
this.setState({
list: [newTask, ...this.state.list],
value: '', // Clear input field
show: true, // Success message
}, ()=>{
window.localStorage.setItem('userTodo', JSON.stringify(this.state.list));
})
}
}
// Handles checkbox
handleInputChange(id) {
this.setState({list: this.state.list.map(item => {
if (item.id === id) {
item.isCompleted = !item.isCompleted;
item.checked = !this.state.checked;
}return item
})}, ()=>{
window.localStorage.setItem('userTodo', JSON.stringify(this.state.list));
})
}
// Delete a task
deleteTask(id){
this.setState({list: this.state.list.filter(item => item.id !== id )},()=>{
window.localStorage.setItem('userTodo', JSON.stringify(this.state.list))
})
console.log(this.state.list)
}
render(){
return(
<div>
<Header />
<Info />
<AddToDo onChange={this.handleChange} value={this.state.value} onSubmit={this.handleSubmit} />
<TodoListItem deleteTask={this.deleteTask} onChange={this.handleInputChange} list={this.state.list} defaultChecked={this.state.checked} />
</div>
)
}
}
export default App;
For TodoListItem.js
import React, { Component } from 'react';
import ToDoItem from './ToDoItem';
import '../sass/main.scss';
class ToDoListItem extends Component{
render(){
const {list, onChange, deleteTask, defaultChecked} = this.props;
return(
<div>
{list.map((todo)=>{
return (
<ToDoItem
key={todo.id}
userTodo={todo.userTodo}
isCompleted={todo.isCompleted}
onChange={onChange}
id={todo.id}
deleteTask={deleteTask}
defaultChecked={defaultChecked}
/>
)
})}
</div>
)
}
}
export default ToDoListItem;
For TodoItem.js
import React, { Component } from 'react';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faTrashAlt } from '#fortawesome/free-solid-svg-icons'
import '../sass/main.scss';
class ToDoItem extends Component{
render(){
const {userTodo, isCompleted, onChange, id, deleteTask, defaultChecked} = this.props;
const checkStyle = isCompleted ? 'completed-todo' : 'not-completed-todo';
return(
<div className={`container ${checkStyle}`}>
<input type="checkbox" onChange={onChange.bind(this, id)} defaultChecked={defaultChecked}/>
<div >
<p className='title'>{userTodo}</p>
</div>
{/* Delete button */}
<button onClick={deleteTask.bind(this, id)}><FontAwesomeIcon className='remove-icon' icon={faTrashAlt} /></button>
</div>
)
}
}
export default ToDoItem;
Please note: I have gone through other questions similar to the problem I am having but I could not solve this problem.
If I did not state the question well, please let me know.
In the below code in App.js,
<TodoListItem deleteTask={this.deleteTask} onChange={this.handleInputChange} list={this.state.list} defaultChecked={this.state.checked} />
You are setting, defaultChecked={this.state.checked} Why do you do that? There is nothing called checked in the state.
In fact, there is no need to pass the defaultValue.
Make the following changes,
In App.js, remove defaultValue prop for TodoListItem
<TodoListItem deleteTask={this.deleteTask} onChange={this.handleInputChange} list={this.state.list}/>
In TodoListItem.js, remove defaultChecked={defaultChecked}
<ToDoItem
key={todo.id}
userTodo={todo.userTodo}
isCompleted={todo.isCompleted}
onChange={onChange}
id={todo.id}
deleteTask={deleteTask}
defaultChecked={defaultChecked} // Remove this.
/>
In ToDoItem.js,
<input type="checkbox"onChange={onChange.bind(this, id)}
defaultChecked={isCompleted} // Replace defaultValue with isCompleted
/>

Reactjs: Using same form for add and update

I crafted a reactjs crud app with help of a tutorial and it works great now. Now i am trying to merge two form together so that same form should be used for both add and update operation.
This is my allpost.js file
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Post from '../components/Post';
import EditComponent from '../components/editComponent';
class AllPost extends Component {
render() {
return (
<div>
<h1>All Posts</h1>
{this.props.posts.map((post) => (
<div key={post.id}>
{post.editing ? <EditComponent post={post} key={post.id} /> :
<Post key={post.id} post={post} />}
</div>
))}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
posts: state
}
}
export default connect(mapStateToProps)(AllPost);
and this is my postForm.js file:
import React, { Component } from 'react';
import { connect } from 'react-redux'
class PostForm extends Component {
handleSubmit = (e) => {
e.preventDefault();
const title = this.getTitle.value;
const message = this.getMessage.value;
const data = {
id: new Date(),
title,
message,
editing: false
}
console.log(data)
this.props.dispatch({
type: 'ADD_POST',
data,
});
this.getTitle.value = '';
this.getMessage.value = '';
}
render() {
return (
<div>
<h1>Create Post</h1>
<form onSubmit={this.handleSubmit}>
<input required type="text" ref={(input)=>this.getTitle = input}
placeholder="Enter Post Title"/>
<br /><br />
<textarea required rows="5" ref={(input)=>this.getMessage = input} cols="28"
placeholder="Enter Post" />
<br /><br />
<button>Post</button>
</form>
</div>
);
}
}
export default connect()(PostForm);
and this is my editComponent.js file
import React, { Component } from 'react';
import { connect } from 'react-redux';
class EditComponent extends Component {
handleEdit = (e) => {
e.preventDefault();
const newTitle = this.getTitle.value;
const newMessage = this.getMessage.value;
const data = {
newTitle,
newMessage
}
this.props.dispatch({ type: 'UPDATE', id: this.props.post.id, data: data })
}
render() {
return (
<div>
<form onSubmit={this.handleEdit}>
<input required type="text" ref={(input) => this.getTitle = input}
defaultValue={this.props.post.title} placeholder="Enter Post Title" /><br /><br />
<textarea required rows="5" ref={(input) => this.getMessage = input}
defaultValue={this.props.post.message} cols="28" placeholder="Enter Post" /><br /><br />
<button>Update</button>
</form>
</div>
);
}
}
export default connect()(EditComponent);
and this is my post.js file:
import React, { Component } from 'react';
import { connect } from 'react-redux'
class Post extends Component {
render() {
return (
<div>
<h2>{this.props.post.title}</h2>
<p>{this.props.post.message}</p>
<button onClick={() => this.props.dispatch({type: 'EDIT_POST', id: this.props.post.id})}>EDIT
</button>
<button onClick={ () => this.props.dispatch({type: 'DELETE_POST', id: this.props.post.id}) }>DELETE
</button>
</div>
);
}
}
export default connect()(Post);
and this is my postReducer.js file:
const postReducer = (state = [], action) => {
switch(action.type) {
case 'ADD_POST':
return state.concat([action.data]);
case 'DELETE_POST':
return state.filter((post)=>post.id !== action.id);
case 'EDIT_POST':
return state.map((post)=>post.id === action.id ? {...post,editing:!post.editing}:post)
case 'UPDATE':
return state.map((post)=>{
if(post.id === action.id) {
return {
...post,
title:action.data.newTitle,
message:action.data.newMessage,
editing: !post.editing
}
} else return post;
})
default:
return state;
}
}
export default postReducer;
Can anyone please help me to achieve this? I tried a lot to use same form form for both add and update and i failed to achieve this.
I think it's better you create separate component for rendering form data(FormComponent) and separate components for edit(EditComponent) and add(AddComponent).
This way there will not be clutter in one component and no if/else conditions for different modes like edit or add, or in future copy mode.
This approach will add flexibility and enhances compositional pattern of react.
1) AddComponent
import React, { Component } from 'react';
import { connect } from 'react-redux'
class AddComponent extends Component {
handleSubmit = (title, message) => {
const data = {
id: new Date(),
title,
message,
editing: false
}
this.props.dispatch({
type: 'ADD_POST',
data,
});
}
render() {
return (
<div>
<h1>Create Post</h1>
<FormComponent
buttonLabel='Post'
handleSubmit={this.handleSubmit}
/>
</div>
);
}
}
export default connect()(AddComponent);
2) EditComponent
import React, { Component } from 'react';
import { connect } from 'react-redux';
class EditComponent extends Component {
handleSubmit = (newTitle, newMessage) => {
const data = {
newTitle,
newMessage
}
this.props.dispatch({ type: 'UPDATE', id: this.props.post.id, data: data })
}
render() {
return (
<div>
<FormComponent
buttonLabel='Update'
handleSubmit={this.handleSubmit}
/>
</div>
);
}
}
export default connect()(EditComponent);
3) FormComponent
import React, { Component } from 'react';
class FormComponent extends Component {
handleSubmit = (e) => {
e.preventDefault();
const title = this.getTitle.value;
const message = this.getMessage.value;
this.props.handleSubmit(title, message);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input required type="text" ref={(input) => this.getTitle = input}
defaultValue={this.props.post.title} placeholder="Enter Post Title" /><br /><br />
<textarea required rows="5" ref={(input) => this.getMessage = input}
defaultValue={this.props.post.message} cols="28" placeholder="Enter Post" /><br /><br />
<button>{this.props.buttonLabel}</button>
</form>
);
}
}
export default FormComponent;
Hope that helps!!!
You can create your own Form component with a prop of editMode to control whether it's Create or Update.
import React, {Component} from 'react';
import PropTypes from 'prop-types';
class Form extends Component {
handleSubmit = e => {
e.preventDefault();
const {editMode, post} = this.props;
const title = this.titleRef.value;
const body = this.bodyRef.value;
if (editMode){
const data = {
title,
body
}
this.props.dispatch({type: 'UPDATE', id: post.id, data})
}
else {
const data = {
id: new Date(),
title,
message,
editing: false
}
this.props.dispatch({type: 'ADD_POST', data});
}
}
render() {
const {editMode, post} = this.props;
const pageTitle = editMode ? 'Edit Post' : 'Create Post';
const buttonTitle = editMode ? 'Update' : 'Post';
return (
<div>
<h1>{pageTitle}</h1>
<form onSubmit={this.handleSubmit}>
<input
required
type="text"
ref={input => this.titleRef = input}
placeholder="Enter Post Title"
defaultValue={post.title}
/>
<textarea
required
rows="5"
ref={input => this.bodyRef = input}
cols="28"
placeholder="Enter Post"
defaultValue={post.body}
/>
<button>{buttonTitle}</button>
</form>
</div>
);
}
}
Form.propTypes = {
editMode: PropTypes.bool,
post: PropTypes.object
}
Form.defaultProps = {
editMode: false, // false: Create mode, true: Edit mode
post: {
title: "",
body: ""
} // Pass defined Post object in create mode in order not to get undefined objects in 'defaultValue's of inputs.
}
export default Form;
It would be on create mode by default but if you wanna update the post you should pass editMode={true} to your form component.

How can I dispatch a state change to redux when calling it from a function inside the component?

I'm still fairly new to Javascript and React, and have been trying to introduce Redux into my admin-site project.
Right now the only thing I want to use it for is to change the ip-address of all axios calls to my backend. So on my admin site, when the user goes to log in, they will be presented with a drop-down of different servers that the site can make a request to, e.g. development, staging, production...
So when the user does log in, any component that needs to make a request, which is actually every single component, can use the IP-address that is stored in the redux state.
A kind of mini-question is I see a lot of people recommend not to connect all components to the redux store, but never sure why, for this, should I just put the IP-address in local-storage and clear() it everytime the user tries to log in? Anyway, I want to at least successfully implement Redux so I can use it in the future if need be.
The problem is that I have some buttons, which DO successfully change the state in the redux-store, and then other components once logged in make the requests to that particular server. But in the dropdown I just put in <Select />, upon any change, it then calls a function inside my component, the function runs and everything gets logged to console as it should, EXCEPT that the state in the redux-store doesnt change anymore, but as I can see it, its still using the same call to react-redux connect as the buttons were doing
So I have my actions.js:
export const DEV_ADDRESS = 'dev.example.com';
export const STAGE_ADDRESS = 'stage.exmaple.com';
export const PROD_ADDRESS = 'prod.example.com';
export function devServer() {
return {
type: DEV_ADDRESS,
};
}
export function stageServer() {
return {
type: STAGE_ADDRESS,
};
}
export function prodServer() {
return {
type: PROD_ADDRESS,
};
}
And my reducer.js:
import {
DEV_ADDRESS,
STAGE_ADDRESS,
PROD_ADDRESS,
} from '../actions/serverActions';
const initialState = {
serverAddress: PROD_ADDRESS,
};
export default function (state = initialState, action) {
switch (action.type) {
case DEV_ADDRESS:
console.log("REDUCER DEV");
return {
...state,
serverAddress: DEV_ADDRESS
};
case STAGE_ADDRESS:
console.log("REDUCER STAGE");
return {
...state,
serverAddress: STAGE_ADDRESS
};
case PROD_ADDRESS:
console.log("REDUCER PROD");
return {
...state,
serverAddress: PROD_ADDRESS
};
default:
return state;
}
}
Now in my LogIn.jsx:
import React, { PureComponent } from 'react';
import { Field, reduxForm } from 'redux-form';
import { connect } from 'react-redux';
import { Button } from 'reactstrap';
import axios from 'axios';
import Select from 'react-select';
import { Redirect } from 'react-router-dom';
import PropTypes from 'prop-types';
import EyeIcon from 'mdi-react/EyeIcon';
import Logo from '../../shared/img/logo/logo_light_2.svg';
import * as serverActionTypes from '../../redux/actions/serverActions';
class LogIn extends PureComponent {
static propTypes = {
handleSubmit: PropTypes.func.isRequired,
};
constructor(props) {
super(props);
this.state = {
showPassword: false,
in: false,
};
}
componentDidMount() {
localStorage.clear();
}
.
.
.
.
.
handleEmailChange(event) {
this.setState({ email: event.target.value })
}
handlePasswordChange(event) {
this.setState({password: event.target.value})
handleChange = (selectedOption) => {
console.log("$$$$$$$$$$$$");
console.log(selectedOption.value);
console.log(this.props)
if (selectedOption.value === serverActionTypes.devServer().type) {
console.log("handle dev change");
{this.props.onSelectDevServerAddress}
}
else if (selectedOption.value === serverActionTypes.stageServer().type) {
console.log("handle stage change");
{this.props.onSelectStageServerAddress}
}
else if (selectedOption.value === serverActionTypes.prodServer().type) {
console.log("handle prod change");
{this.props.onSelectProdServerAddress}
}
};
render() {
const { handleSubmit } = this.props;
if (this.state.in === true) {
return <Redirect to={{pathname: "/dashboard"}} />;
}
return (
<div className="account">
<div className="account__wrapper">
<div className="account__card" align="center">
<img
src={Logo}
alt="example-logo"
height="35"
style={{marginBottom: '50px'}}
/>
</div>
<div className="account__card">
<form className="form form--horizontal" onSubmit={handleSubmit}>
<div className="form__form-group">
<span className="form__form-group-label">E-mail</span>
<div className="form__form-group-field">
<Field
name="email"
component="input"
type="email"
placeholder="example#mail.com"
value={this.state.email}
onChange={this.handleEmailChange.bind(this)}
/>
</div>
</div>
<div className="form__form-group">
<span className="form__form-group-label">Password</span>
<div className="form__form-group-field">
<Field
name="password"
component="input"
type={this.state.showPassword ? 'text' : 'password'}
placeholder="Password"
value={this.state.password}
onChange={this.handlePasswordChange.bind(this)}
/>
<button
className={`form__form-group-button${this.state.showPassword ? ' active' : ''}`}
onClick={e => this.showPassword(e)}
><EyeIcon />
</button>
</div>
</div>
<div className="form__form-group">
<span className="form__form-group-label">Server</span>
<div className="form__form-group-field">
<div className="form__form-group-input-wrap">
<Select
name='server_address_selector'
value='prod.example.com'
onChange={this.handleChange}
options={[
{ value: 'dev.example.com', label: 'Dev' },
{ value: 'stage.example.com', label: 'Stage' },
{ value: 'prod.example.com', label: 'Prod' },
]}
clearable={false}
className="form__form-group-select"
/>
</div>
</div>
</div>
<Button
color="success"
onClick={this.onLogin.bind(this)}
outline>
Sign In
</Button>
<Button
color="success"
onClick={this.props.onSelectDevServerAddress}
outline>
DEV
</Button>
<Button
color="success"
onClick={this.props.onSelectStageServerAddress}
outline>
STAGE
</Button>
<Button
color="success"
onClick={this.props.onSelectProdServerAddress}
outline>
PROD
</Button>
</form>
</div>
</div>
</div>
);
}
}
const mapStateToProps = state => {
return {
ipAddress: state.server
}
}
const mapDispatchToProps = dispatch => ({
onSelectDevServerAddress: () => dispatch(serverActionTypes.devServer()),
onSelectStageServerAddress: () => dispatch(serverActionTypes.stageServer()),
onSelectProdServerAddress: () => dispatch(serverActionTypes.prodServer()),
});
LogIn = connect(
mapStateToProps,
mapDispatchToProps
)(LogIn);
export default reduxForm({
form: 'log_in_form'
})(LogIn);
The problem is that you didn't dispatch any action to the reducer. You have to actually invoke a functions from mapStateToDispatch in order to perform some update on the redux store state.
In the code below:
handleChange = (selectedOption) => {
console.log("$$$$$$$$$$$$");
console.log(selectedOption.value);
console.log(this.props)
if (selectedOption.value === serverActionTypes.devServer().type) {
console.log("handle dev change");
{this.props.onSelectDevServerAddress}
}
else if (selectedOption.value === serverActionTypes.stageServer().type) {
console.log("handle stage change");
{this.props.onSelectStageServerAddress}
}
else if (selectedOption.value === serverActionTypes.prodServer().type) {
console.log("handle prod change");
{this.props.onSelectProdServerAddress}
}
};
Instead of doing something like this:
{this.props.onSelectProdServerAddress}
You have to call that function:
this.props.onSelectProdServerAddress();
Now, your action will be dispatched to the reducer.

How to log data from input onto the console in React

I would like to know how to have it where when I press enter on my keyboard, it logs whatever is in the input onto the console. Please help. Thanks!
import React, { Component } from 'react';
import './App.css';
class App extends Component {
onClick() {
alert("CLICKED");
}
onChange(eve) {
console.log(eve.target.value);
}
onSubmit() {
alert("SUBMITTED");
}
render() {
const list = ["Lebron", "Kobe", "Steph", "Kevin"];
return (
<div className="App">
<h1>{
list.map(listitem =>{
return (
<div onClick={this.onClick}>
{listitem}
</div>
)})
}</h1>
<form onSubmit={this.onSubmit}>
<input onChange={this.onChange} />
</form>
</div>
);
}}
export default App;
Please help!
Store the input value in a state variable onChange and then log it to the console onSubmit.
class App extends Component {
constructor(props) {
super(props);
this.state = {
value: ''
};
}
onChange = event => {
this.setState({ value: event.target.value});
}
onSubmit = event => {
const { value } = this.state;
event.preventDefault();
console.log(value);
}
render() {
const list = ["Lebron", "Kobe", "Steph", "Kevin"];
const { value } = this.state;
return (
<div className="App">
...
<form onSubmit={this.onSubmit}>
<input onChange={this.onChange} value={value}/>
</form>
</div>
);
}
}

Resources