I'm trying to set up my search so when I click enter it will begin to search and redirect to the search page. I was looking through the documentation and it wasn't clear how to set this up. How can I set up pressing enter to begin the search? I'm having a tough time figuring this out, even though I think it should be simple.
class SearchBar extends Component {
constructor(props) {
super(props)
this.state = {query: '', results: [], isLoading: false}
}
componentWillMount() {
this.resetComponent()
}
resetComponent = () => this.setState({ isLoading: false, results: [], value: '' })
search(query) {
this.setState({ query });
axios
.get(`/api/search?query=${query}`)
.then(response => {
console.log(query);
this.setState({ results: response.data});
})
.catch(error => console.log(error));
}
handleSearchChange = (query) => {
this.search(query);
this.setState({ isLoading: true, query })
setTimeout(() =>
this.setState({
isLoading: false,
}) , 300)
}
handleResultSelect = (e, { result }) => this.setState({ query: result} )
render () {
const resultRenderer = ({ title }) => <List content = {title}/>
return (
<Search
loading={this.state.isLoading}
onResultSelect={this.handleResultSelect}
onSearchChange={(event) => {this.handleSearchChange(event.target.value)}}
showNoResults={false}
query={this.props.query}
selectFirstResult = {true}
resultRenderer={resultRenderer}
results ={this.state.results}
{ ...this.props} />
);
}
}
export default SearchBar
Thanks!
Here is a minimal example of how you can do this.
import React from 'react'
import { Form, Input } from 'semantic-ui-react';
class FormExampleForm extends React.Component {
constructor(props) {
super(props);
this.state = {
query: ''
}
}
handleFormSubmit = () => {
console.log('search:', this.state.query);
}
handleInputChange = (e) => {
this.setState({
query: e.target.value
});
}
render() {
return (
<Form onSubmit={this.handleFormSubmit}>
<Form.Input placeholder='Search...' value={this.state.query} onChange={this.handleInputChange} />
</Form>
)
}
}
export default FormExampleForm;
Here is a working example:https://stackblitz.com/edit/react-q5wv1c?file=Hello.js
Modify the Search component in semantic-ui react source code to implement the onKeyPress handler
Related
I am new at react and
I have been trying to apply event handling in react but facing some Problem on onclick event.
When the button "show me of germany" is called the Page then stuck to loading only ...
Here is the code i have written ..
class App extends Component {
constructor(props) {
super(props);
this.state = { articles: [],
isLoaded:false ,
country:'us'
}
this.change = this.change.bind(this);
}
componentDidMount() {
const APIurl = `https://newsapi.org/v2/top-headlines?country=${this.state.country}&apiKey=${API_KEY}`;
fetch(APIurl)
.then(response => response.json())
.then(json => {
this.setState({
articles: json.articles,
isLoaded:true
})
})
}
// function to change the state
change()
{
this.setState({
articles: [],
isLoaded:false ,
country:"de"
})
}
render() {
const { isLoaded,articles } = this.state;
if(!isLoaded)
{
return (<h1>Loading....</h1>)
}
return (
<div>
<Navbar/>
<button onClick={this.change}>show me of germany</button>
<ul>
{articles.map(item=>(
<News item={item}/>
))}
</ul>
</div>
);
}
}
export default App;
Hope you understood the problem
You have to do request again.
class App extends Component {
constructor(props) {
super(props);
this.state = {
articles: [],
isLoaded: false,
country:'us'
}
this.change = this.change.bind(this);
}
componentDidMount() {
fetchData(this.state.country);
}
componentDidUpdate(prevProps, prevState) {
const { country: prevCountry } = prevState;
const { country: nextCountry } = this.state;
if (prevCountry !== nextCountry) {
fetchData(nextCountry);
}
}
change() {
this.setState({ country: 'de' });
}
fetchData(country) {
this.setState({ articles: [], isLoaded: false });
fetch(
`https://newsapi.org/v2/top-headlines?country=${country}&apiKey=${API_KEY}`
)
.then(res => res.json())
.then(({ articles }) => {
this.setState({ articles, isLoaded: true });
})
.catch(console.error);
}
render() {
//...
}
}
export default App;
I'm trying to do a basic API fetch and show that information onClick using a button called GENERATE. All it should do for now is show the first url in the json I receive.
Once that is achieved, I want it to show the next url on each click.
App.js
import React, { Component } from 'react';
import { ThemeProvider, createToolkitTheme } from 'internaltools/theme';
import { AppHeader } from 'internaltools/app-header';
const LIGHT_THEME = createToolkitTheme('light');
const DARK_THEME = createToolkitTheme('dark');
const API = 'https://hn.algolia.com/api/v1/search?query=';
const DEFAULT_QUERY = 'redux';
class App extends Component {
constructor(props) {
super(props);
this.state = {
hits: [],
isLoading: false,
error: null,
};
}
componentDidMount(){
this.setState({ isLoading: true });
fetch(API + DEFAULT_QUERY)
.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error('Something went wrong with the API...');
}
})
.then(data => this.setState({ hits: data.hits[0], isLoading: false }))
.catch(error => this.setState({ error, isLoading: false }));
}
render() {
const { hits, isLoading, error } = this.state;
return (
<>
<button onClick={hits.url}>GENERATE</button>
</>
);
}
}
Please help me find out why my button doesn't work. And how do I iterate over the urls on each click, i.e. show the next url from the json on each click. Thanks.
You should pass a function name to your onClick handler. Then in that function you can access the data you wanted.
enter code here
import React, { Component } from 'react';
import { ThemeProvider, createToolkitTheme } from 'internaltools/theme';
import { AppHeader } from 'internaltools/app-header';
const LIGHT_THEME = createToolkitTheme('light');
const DARK_THEME = createToolkitTheme('dark');
const API = 'https://hn.algolia.com/api/v1/search?query=';
const DEFAULT_QUERY = 'redux';
class App extends Component {
constructor(props) {
super(props);
this.state = {
hits: [],
isLoading: false,
error: null,
hitsCount: 0
};
this.handleClick = this.handleClick.bind(this);
}
componentDidMount(){
this.setState({ isLoading: true });
fetch(API + DEFAULT_QUERY)
.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error('Something went wrong with the API...');
}
})
.then(data =>
this.setState({ hits: data.hits, hitsCount: 0 ,isLoading: false
}))
.catch(error => this.setState({ error, isLoading: false }));
}
handleClick(){
this.setState(prevState => ({ hitsCount: prevState.hitsCount + 1
}));
}
render() {
const { hits, hitsCount, isLoading, error } = this.state;
return (
<>
<div>
count: {hitsCount}
url: {hits[hitsCount].url}
</div>
<button onClick={this.handleClick}>GENERATE</button>
</>
);
}
}
You need to pass an onClick handler function to update a state value.
Here's a codesandbox that stores the hits array in state along with a current index, and a handler that simply increments the index.
Consider This:
Read through the comments in the code to get the updates.
class App extends Component {
constructor(props) {
super(props);
this.state = {
hits: [],
currentHit: 0, //add a state currentHit to hold the url that is displayed by now
isLoading: false,
error: null,
};
}
componentDidMount(){
this.setState({ isLoading: true });
fetch(API + DEFAULT_QUERY)
.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error('Something went wrong with the API...');
}
})
.then(data => this.setState({ hits: data.hits, isLoading: false })) //Make hits array holding all the hits in the response instead of only the first one
.catch(error => this.setState({ error, isLoading: false }));
}
handleClick = () => {
this.setState(prevState => ({
currentHit: prevState.currentHit + 1,
}));
}
render() {
const { hits, isLoading, error, currentHit } = this.state;
// pass the handleClick function as a callback for onClick event in the button.
return (
<>
<p>{hits[currentHit].url}<p/>
<button onClick={this.handleClick.bind(this)}>GENERATE</button>
</>
);
}
}
Here is the working code, on each click next url will be shown.
codesandbox link
handleChange method can work if you want to append the url from array as well. Or you could just increment the index in this function.
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
state = {
data: [],
index: 0
};
componentDidMount() {
this.setState({ isLoading: true });
fetch("https://reqres.in/api/users")
.then(response => {
if (response) {
return response.json();
} else {
throw new Error("Something went wrong with the API...");
}
})
.then(data => this.setState({ data: data.data }))
.catch(error => this.setState({ error }));
}
handleChange = () => {
let i =
this.state.index < this.state.data.length ? (this.state.index += 1) : 0;
this.setState({ index: i });
};
render() {
return (
<div className="App">
<span>
{this.state.data.length && this.state.data[this.state.index].avatar}
</span>
<button onClick={this.handleChange}>GENERATE</button>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
I am trying to populate a semantic-ui-react drop-down with the first value(from the options parameter). But I am unable to do the same . Values for this dropdown I am fetching it from backend and then mapping it to a format supported by semantic-ui-react select.
Help would be appreciated.
//state object
this.state = {
users: [] ,
selectedUser: '',
defaultUser: string
}
//fetch call to get the values and then transforming
async componentDidMount()
{
try {
let data = await fetch('/api/fetch/users')
let users = await data.json();
users = users.map((obj:any) => ({ //formatting the list to support options array of semnatic-ui-react select
key: obj.id,
text: obj.name,
value: obj.name
}));
this.setState({users},defaultUser: users[0].value); //setting the dropdown with first value, but it is not happening
}
catch(e){
console.log('Error', e.message);
}
}
//onchange handler
dropdownChange = (event: React.SyntheticEvent<HTMLElement>, data:any) => {
this.setState(prevState => ({
[data.name]: data.value
}));
}
//Inside render, Select
<Select
options={this.state.users}
name="selectedUser"
value={this.state.selectedUser}
onChange={this.dropdownChange}
defaultValue = {this.state.defaultUser}
/>
I think you don't need the defaultValue prop. You can check out the codesandbox bellow. I used react hooks but you get the point.
https://codesandbox.io/s/heuristic-wiles-eq7su
For your particular code:
import React from "react";
import ReactDOM from "react-dom";
import { Select } from "semantic-ui-react";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
users: [],
selectedUser: ""
};
}
async componentDidMount(){
try {
let data = await fetch('/api/fetch/users')
let users = await data.json();
users = users.map((obj:any) => ({
key: obj.id,
text: obj.name,
value: obj.name
}));
this.setState({users}, () => {
this.setState({selectedUser: this.state.users[0].value});
})
}
//onchange handler
dropdownChange = (event, data) => {
this.setState({ selectedUser: data.value });
};
render() {
return (
<Select
onChange={this.dropdownChange}
placeholder="Select a user"
value={this.state.selectedUser}
selection
options={this.state.users}
/>
);
}
}
Help me out, I am new to React and Javascript
Getting this error:"TypeError: _this.props.onCreate is not a function" although the function has been passed in the props and has been bound.
Here is my current code in react.
UserCreate.js
import React, { Component } from 'react';
class UserCreate extends Component {
constructor(props){
super(props);
this.state = {
email: ''
};
}
handleChange = email => event => {
this.setState(
{
[email]: event.target.value,
}
)
}
handleCreate = () => {
console.log('create', this.state.email);
this.props.onCreate({'email': this.state.email});
}
render() {
let userData = this.props.user && this.props.user.email;
return (
<div>
<h3> New User Form </h3>
<input onChange={this.handleChange('email')} placeholder="Email"/>
<button onClick={this.handleCreate}>Create</button>
</div>
);
}
}
export default UserCreate;
App.js
const USerCreateWithData = compose(
graphql(UserCreateMutation, {
props: (props) => ({
onCreate: (user) => {
props.mutate({
variables: { ...user },
optimisticResponse: () => ({ createUser: { ...user, __typename: 'User'}})
})
}
}
),
options: {
update: (dataProxy, { data: { createUser }}) => {
}
}
})
)(UserCreate);
UserCreateMutation
export default gql`
mutation UserCreateMutation($email: String!){
createUser(
email: $email
) {
__typename
id
email
}
}
`;
What I am doing wrong in here? I have tried every solutions that I have seen on google, stackoverflow but haven't found a solution yet.
I've created a container called siteEdit.js. It handles creating & editing "sites".
I've setup actionCreators that handles taking the form data and submitting it to the API. This works perfectly.
But when you visit the container using a route that contains an ID it will run an actionCreator that will fetch for the "Site" data based on the id param.
This all works as expected, but since I'm using redux, I'm setting the Input value with the props. for example, this.props.title
I'm trying to stay away from using the redux-form package for now.
Container:
import React, {Component} from 'react';
import { connect } from 'react-redux';
import {createSite, getSite} from '../../actions/siteActions';
class SiteEdit extends Component {
constructor(props) {
super(props)
this.state = {
title: '',
url: '',
description: '',
approvedUsers: []
}
this.handleSubmit = this.handleSubmit.bind(this)
this.handleInputChange = this.handleInputChange.bind(this)
}
componentWillMount() {
if(this.props.params.id) {
this.props.dispatch(getSite(this.props.params.id))
}
}
handleInputChange(e) {
const target = e.target
const value = target.type === 'checkbox' ? target.checked : target.value
const name = target.name
this.setState({
[name]: value
})
}
handleSubmit(e) {
e.preventDefault()
this.props.dispatch(createSite(this.state))
}
render() {
const {title, url, description, approvedUsers} = this.props
return (
<div className="SiteEdit">
<h1>NEW SITE</h1>
<form onSubmit={this.handleSubmit}>
<div className="block">
<label>Site Name</label>
<input
className="input"
type="text"
value={title ? title : this.state.title}
onChange={this.handleInputChange}
name="title" />
</div>
<div className="block">
<label>Site URL</label>
<input
className="input"
type="text"
value={this.state.url}
onChange={this.handleInputChange}
name="url" />
</div>
<div className="block">
<label>Description</label>
<input
className="textarea"
type="textarea"
value={this.state.description}
onChange={this.handleInputChange}
name="description" />
</div>
<div className="block">
<label>Approved Users</label>
<input
className="input"
type="text"
value={this.state.approvedUsers}
onChange={this.handleInputChange}
name="approvedUsers" />
</div>
<button className="button--action">Create</button>
</form>
</div>
)
}
}
const mapStateToProps = (state) => ({
title: state.sites.showSite.title,
url: state.sites.showSite.url,
description: state.sites.showSite.description,
approvedUsers: state.sites.showSite.approvedUsers
})
SiteEdit = connect(mapStateToProps)(SiteEdit)
export default SiteEdit
ActionCreators:
import config from '../config'
import { push } from 'react-router-redux'
const apiUrl = config.api.url
// List all sites
export const LIST_SITES_START = 'LIST_SITES_START'
export const LIST_SITES_SUCCESS = 'LIST_SITES_SUCCES'
export const LIST_SITES_ERROR = 'LIST_SITES_ERROR'
export function sitesListStart(data) {
return { type: LIST_SITES_START, data }
}
export function sitesListSuccess(data) {
return { type: LIST_SITES_SUCCESS, data }
}
export function sitesListError(data) {
return { type: LIST_SITES_ERROR, data }
}
export function listSites() {
return (dispatch) => {
dispatch(sitesListStart())
fetch(`${apiUrl}/listSites`)
.then(res => res.json())
.then(json => {
dispatch(sitesListSuccess(json))
})
.catch(error => {
dispatch(sitesListError)
})
}
}
// Create & Edit Sites
export const CREATE_SITE_START = 'CREATE_SITE_START'
export const CREATE_SITE_SUCESS = 'CREATE_SITE_SUCCESS'
export const CREATE_SITE_ERROR = 'CREATE_SITE_ERROR'
export function siteCreateStart(data) {
return { type: CREATE_SITE_START, data}
}
export function siteCreateSuccess(data) {
return { type: CREATE_SITE_SUCCESS, data}
}
export function siteCreateError(error) {
return { type: CREATE_SITE_ERROR, error}
}
export function createSite(data) {
return (dispatch) => {
dispatch(siteCreateStart())
fetch(`${apiUrl}/createSite`, {
method: 'post',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(res => res.json())
.then(json => {
dispatch(push('/'))
dispatch(siteCreateSuccess())
})
.catch(error => {
dispatch(siteCreateError())
})
}
}
// Get Single Site
export const GET_SITE_START = 'GET_SITE_START'
export const GET_SITE_SUCCESS = 'GET_SITE_SUCCESS'
export const GET_SITE_ERROR = 'GET_SITE_ERROR'
export function getSiteStart(data) {
return { type: GET_SITE_START, data}
}
export function getSiteSuccess(data) {
return { type: GET_SITE_SUCCESS, data}
}
export function getSiteError(error) {
return { type: GET_SITE_ERROR, error}
}
export function getSite(id) {
return (dispatch) => {
dispatch(getSiteStart())
fetch(`${apiUrl}/getSite/${id}`)
.then(res => res.json())
.then(json => {
dispatch(getSiteSuccess(json))
})
.catch(error => {
dispatch(getSiteError())
})
}
}
Reducers:
import {push} from 'react-router-redux'
import {
LIST_SITES_START,
LIST_SITES_SUCCESS,
LIST_SITES_ERROR,
GET_SITE_START,
GET_SITE_SUCCESS,
GET_SITE_ERROR
} from '../actions/siteActions'
const initialState = {
sitesList: {
sites: [],
error: null,
loading: true
},
showSite: {
title: '',
url: '',
description: '',
approvedUsers: [],
loading: true
}
}
export default function (state = initialState, action) {
switch (action.type) {
// List Sites
case LIST_SITES_START:
return Object.assign({}, state, {
sitesList: Object.assign({}, state.sitesList, {
loading: true
})
})
case LIST_SITES_SUCCESS:
return Object.assign({}, state, {
sitesList: Object.assign({}, state.sitesList, {
sites: action.data,
loading: false
})
})
case LIST_SITES_ERROR:
return Object.assign({}, state, {
error: action.error,
loading: false
})
case GET_SITE_START:
return Object.assign({}, state, {
showSite: Object.assign({}, state.showSite, {
loading: true
})
})
case GET_SITE_SUCCESS:
return Object.assign({}, state, {
showSite: Object.assign({}, state.showSite, {
...action.data,
loading: false
})
})
case GET_SITE_ERROR:
return Object.assign({}, state, {
showSite: Object.assign({}, state.showSite, {
error: action.error,
loading: false
})
})
default:
return state
}
}
You are setting the ternary for the value with the props.title taking precedent, like so just to re-iterate -
const { title } = this.props;
...
value={title ? title : this.state.title}
Your onChange logic is correct and is probably updating your components local state correctly, however you still have this.props.title, so it will take precedent in that ternary.
There are a bunch of ways you could handle this, it will depend on order of operations really (that is when props.title will be truthy or not). Assuming you have the title when the component mounts you can do something in the constructor like:
constructor(props) {
super(props)
this.state = {
title: props.title, << set default here
url: '',
description: '',
approvedUsers: []
}
then in the input you only need to set the value to the state title
value={this.state.title}
This will depend on when the props.title value comes into your component of course, if it is not there for mount, this will not work as intended.
You could also pass a function to evaluate all of this for the value of the input as well - inside of which you would more verbosely check the props.title vs the state.title and decide which one to return as your value.
<input value={this.returnTitleValue} .. << something like so
Hope this helps!