I want to pass values from SearchInput (parent) to FetchData (child) component. It does not work properly, because I have to click twice to fetch data and this.props.loaded should be true after click on submit button. I know, that I should use callback function, but I dont know, which function and where. I'm started to learn ReactJS a week ago.
import React, {Component} from "react";
import FetchData from "./FetchData";
export default class SearchInput extends Component {
constructor(props) {
super(props);
this.state = {
cityName: "",
loaded: false
}
this.handleCityNameChange = this
.handleCityNameChange
.bind(this);
this.handleSubmitButton = this
.handleSubmitButton
.bind(this);
}
handleCityNameChange = (e) => {
const val = e.target.value;
this.setState({cityName: val});
e.preventDefault();
}
handleSubmitButton = (e) => {
//const val = document.getElementById("search").value;
this.setState({cityName: this.state.cityName, loaded: true});
e.preventDefault();
}
render() {
const {cityName, loaded} = this.state;
return (
<div>
<form>
<label htmlFor="search">Search city:</label>
<input
type="text"
name="search"
id="search"
value={this.state.cityName}
onChange={this.handleCityNameChange}/>
<input type="submit" onClick={this.handleSubmitButton}/>
</form>
<FetchData
cityName={cityName}
loaded={loaded}
handleSubmitButton={this.handleSubmitButton}/>
</div>
)
}
}
import React, {Component} from "react";
import axios from "axios";
import DisplayWeather from "./DisplayWeather";
export default class FetchData extends Component {
constructor(props) {
super(props);
this.state = {
descriptionMain: "",
description: "",
temperature: null,
weatherIcon: ""
}
}
// otherFunc() {
// this.props.handleSubmitButton();
// }
fetchData = () => {
if (this.props.loaded) {
const apiURL = `https://api.openweathermap.org/data/2.5/weather?q=${this.props.cityName}&units=metric&APPID=e6f4d816d3ade705ec1d8d9701b61e14`;
console.log(apiURL)
axios
.get(apiURL)
.then(res => {
this.setState({descriptionMain: res.data.weather[0].main, description: res.data.weather[0].description, temperature: res.data.main.temp, weatherIcon: res.data.weather[0].icon});
})
}
}
componentWillReceiveProps() {
this.fetchData();
}
render() {
return (
<div>
<DisplayWeather {...this.state} cityName={this.props.cityName}/>
</div>
)
}
}
export default class DisplayWeather extends Component {
render() {
const {descriptionMain, description, temperature, weatherIcon, cityName} = this.props;
return (
<div>
<h3>{cityName}</h3>
<h4>Sky: {description}</h4>
<h5>Description: {descriptionMain}</h5>
<span className="temperature">{temperature}
°C</span>
<img
src={`http://openweathermap.org/img/w/${weatherIcon}.png`}
alt={`${description}`}/>
</div>
)
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
You have to enclose the submit attribute in an arrow function:
onClick{()=>this.handleSubmutButton()}
Do the same for on change as well
Related
So I'm trying to create a list app using React. I don't have any errors or warnings, and my input bar is showing, but my textList won't render. In other words when ever I hit enter, the text info I put in the input bar won't create a list as expected.
I tried using keyCode === 13 and onKeyDown, but it's not having any effect on the app. What am I missng?
Here's my code:
filtered-input.js
import React, { Component } from "react";
import "./filtered-input.css";
import ItemList from "./item-list";
export class FilteredInput extends Component {
constructor(props) {
super(props);
this.state = {
textList: [],
};
}
handleChange(e) {
this.setState({ value: e.target.value });
}
handleAdd = (e) => {
if (e.keyCode === 13) {
let lists = this.props.state.textList;
lists.push(this.props.state.value);
this.setState({ textList: lists });
}
};
render() {
console.log("Here comes state -->");
console.log(this.state);
return (
<div>
<input
className="filtered-input-box"
type="text"
onKeyDown={this.props.handleAdd}
value={this.state.value}
onChange={this.props.handleChange}
/>
<ItemList item={this.state.textList}></ItemList>
</div>
);
}
}
ItemList.js
import React, { Component } from 'react';
export class ItemList extends Component {
render() {
const items = this.props.item.map((item) =>
<li>{item}</li>
);
return (
<ul>
{items}
</ul>
);
}
}
export default ItemList;
App.js
import React, { Component } from 'react';
import { FilteredInput } from './filtered-input.js';
export class App extends Component {
render() {
return (
<div>
<p>This page demonstrates a component that maintains its own state.</p>
<FilteredInput />
</div>
);
}
}
There were small syntactical mistakes here and there, apart from that everything looks perfect:
import React, { Component } from "react";
import "./style.css";
export default class App extends Component {
render() {
return (
<div>
<p>This page demonstrates a component that maintains its own state.</p>
<FilteredInput />
</div>
);
}
}
class FilteredInput extends Component {
constructor(props) {
super(props);
this.state = {
textList: [],
value: ""
};
}
handleChange = e => {
console.log(e.target.value);
this.setState({ value: e.target.value });
};
handleAdd = e => {
console.log("hi");
if (e.keyCode === 13) {
let lists = this.state.textList;
lists.push(this.state.value);
this.setState({ textList: lists });
}
};
render() {
console.log("Here comes state -->");
console.log(this.state);
return (
<div>
<input
className="filtered-input-box"
type="text"
onKeyDown={this.handleAdd}
value={this.state.value}
onChange={this.handleChange}
/>
<ItemList item={this.state.textList} />
</div>
);
}
}
class ItemList extends Component {
render() {
const items = this.props.item?.map(item => <li>{item}</li>);
return <ul>{items}</ul>;
}
}
Full working app : Stackblitz
I am calling 'reload' function when the value is changed on react-select component. The control is going into the function but it is not returning the component from that function. I have tried calling the component from render then the component is being called.
import React, { Component } from 'react';
import Select from 'react-select';
import removeDuplicates from '../../helpers.js';
import Dashboard from '../../containers/Dashboard';
class ExportDropDown extends Component {
constructor() {
super();
this.state = { dropdown: null }
this.reload = this.reload.bind(this);
}
reload(value) {
console.log('val', value);
const filter = (
<div className={'lll'}>
<Dashboard filter={'abc'} />
</div>
);
return filter;
}
render() {
const uniqueArray = removeDuplicates(this.props.data, 'Expert');
return (
<div>
<select onChange={this.reload}>
{uniqueArray.map((d) => <option key={d.Expert} value={d.Expert}>{d.Expert}</option>)}
</select>
</div>
);
}
}
export default ExportDropDown;
Here are a few fixes on your code. Please take a look to understand how:
1 - We control the select input value by keeping it's value in our state. (onSelectValueChange)
2 - How our conditional dashboard component renders with the current select input value
import React, { Component } from 'react'
import Select from 'react-select'
import removeDuplicates from '../../helpers.js'
import Dashboard from '../../containers/Dashboard'
class ExportDropDown extends Component {
constructor(props) {
super(props)
this.state = {
dropdownValue: null
}
this.onSelectValueChange = this.onSelectValueChange.bind(this)
}
onSelectValueChange (event) {
this.setState({
dropdownValue: event.target.value
})
}
render() {
const uniqueArray = removeDuplicates(this.props.data, 'Expert')
return (
<div>
<select
onChange={this.onSelectValueChange}
value={dropdownValue}
>
{
uniqueArray.map(
(d) => <option key={d.Expert} value={d.Expert}>{d.Expert}</option>
)
}
</select>
{
dropdownValue &&
<div className={'lll'}>
<Dashboard filter={dropdownValue} />
</div>
}
</div>
)
}
}
export default ExportDropDown
You try to return a value to the onChange callback which doesn't expect a return value. Only the return value of your render hook will be rendered.
You could keep the dropdown value in your state and decide in your render hook what to render:
render() {
const filterElem = this.createFilter();
...
return (
<div>
Something
...
{ filterElem }
</div>
);
}
reload(event) {
const { value: dropDownValue } = event.target;
this.setState({ dropDownValue });
}
createFilter() {
const { dropDownValue } = this.state;
return dropDownValue ? <MyFilterComponent /> : null;
}
Change this in select tags:
onChange= { (e) => { this.reload(e.target.value) } }
I'm a bit new to React and Firestore and already trying to figure out what is happening for a couple of hours. I Try to make my filter function working with data which I receive from Firestore in APP.js. I pass the data {tasks, searchTerm} to DASHBOARD component. The filter worked before when using state and props, but after replacing the hard-coded data in state with firestore data, it doesn't work anymore and I get the following error when filtering the array in the DASHBOARD component:
Cannot read property 'toLowerCase' of undefined
I've tried to send the data without any filtering directly to TASKS.js and this is working correctly (all the tasks are shown). But as soon as I pass newArray to , it doesn't work anymore.
Also, when logging task.title in tasks.filter function in the DASHBOARD component, it shows all the data (with a little delay because the data is coming from Firestore)
APP.JS -
import React, { Component } from 'react';
import './App.css';
import Dashboard from './Components/Dashboard/Dashboard'
import AddTask from './Components/Tasks/Task/AddTask'
import Navbar from './Components/Navbar/Navbar'
import Searchbar from './Components/Searchbar/Searchbar'
import firebase from './Firebase';
class App extends Component {
constructor(props) {
super(props)
this.ref = firebase.firestore().collection('tasks')
this.state = {
tasks: [],
searchTerm: ""
}
this.handleLikeButton = this.handleLikeButton.bind(this)
this.handleRemoveButton = this.handleRemoveButton.bind(this)
this.addTask = this.addTask.bind(this)
this.handleFilter = this.handleFilter.bind(this)
}
componentWillMount() {
const db = firebase.firestore()
const allTasks = []
db.collection('tasks').onSnapshot(collection => {
const tasks = collection .docs.map(doc => doc.data())
this.setState({ tasks: tasks, searchTerm: "" })
})
}
handleLikeButton = (task) => (e) => {
const tasks = [...this.state.tasks]
const index = tasks.indexOf(task)
tasks[index].likes++
this.setState({
tasks: tasks
})
}
addTask = (taskName) => (e) => {
this.ref.add({
id: Math.floor(Math.random() * 100000000000000),
title: taskName,
likes: 0
})
}
handleRemoveButton = (removingTask) => (e) => {
const tasks = [...this.state.tasks]
const newTasks = tasks.filter(task => removingTask.id !== task.id)
this.setState({
tasks: newTasks
})
}
handleFilter = (searchTerm) => {
this.setState({
searchTerm: searchTerm
})
}
render() {
return (
<div className="App">
<Navbar />
<Searchbar handleFilter={this.handleFilter} />
<AddTask addTask={this.addTask} />
<Dashboard tasks={this.state.tasks} searchTerm={this.state.searchTerm} handleLikeButton={this.handleLikeButton} handleRemoveButton={this.handleRemoveButton}/>
</div>
);
}
}
export default App;
DASHBOARD.JS -
import React, { Component } from 'react'
import Tasks from '../Tasks/Tasks'
class Dashboard extends Component {
constructor(props) {
super(props)
this.filterTasks = this.filterTasks.bind(this)
}
filterTasks = () => {
const tasks = [...this.props.tasks]
const newArray = tasks.filter(task =>
task.title.toLowerCase().indexOf(this.props.searchTerm.toLowerCase()) > -1)
return (
<Tasks tasks={newArray} handleLikeButton={this.props.handleLikeButton} handleRemoveButton={this.props.handleRemoveButton} />
)
}
render() {
return (
<div>
<h2>Dashboard</h2>
{this.filterTasks()}
</div>
)
}
}
export default Dashboard
ADDTASK.JS
import React, { Component } from 'react'
class AddTask extends Component {
constructor(props) {
super(props)
this.state = {
addNewTaskFieldEmpty: true,
taskName: ""
}
this.onChangeHandler = this.onChangeHandler.bind(this)
this.disableButton = this.disableButton.bind(this)
}
onChangeHandler(e) {
this.setState({
taskName: e.target.value,
})
this.disableButton(e.target.value)
}
disableButton(taskName) {
if(taskName.length == 0) {
this.setState({addNewTaskFieldEmpty: true})
} else {
this.setState({addNewTaskFieldEmpty: false})
}
}
render() {
return (
<div>
<div className="mdc-text-field half-size">
<input className="mdc-text-field__input " onChange={this.onChangeHandler} />
<div className="mdc-line-ripple"></div>
<label className="mdc-floating-label">Task Name</label>
</div>
<a className={"btn-floating btn-large waves-effect waves-light red " + (this.state.addNewTaskFieldEmpty ? 'disabled' : '')} onClick={this.props.addTask(this.state.taskName)}><i className="material-icons">add</i></a>
</div>
)
}
}
export default AddTask
Lint your App.css for any errors.
I encountered this message. I traced it to a CSS include:
.box-table { border-color:; border: 1px solid #dbdad8; }
The missing value of border-color: caused npm run build to fail.
Interestingly, the same file contained
.submenu-button.submenu-opened:after { background:; }
which caused no problems at all.
I am in the process of learning graphql and react-apollo. I have set up a search query in my code. I am unsure how to pass a variable from my code (i.e. this.state.search) to my grapnql call.
I have looked at many answers including this one, but it seems a bit different.
The docs also don't seem to give any guidance on how to use state as the variable.
My code is below.
Can anyone advise how to connect both of these?
import React, { Component} from 'react'
import { graphql } from 'react-apollo'
import gql from 'graphql-tag'
class Search extends Component {
constructor(props) {
super(props)
this.state = {
search: ''
}
}
updateSearch = (e) => {
this.setState({
search: e.target.value
})
}
submitSearch = (e) => {
e.preventDefault()
console.log(this.state)
}
render() {
const { search } = this.state;
return (
<form onSubmit={ this.submitSearch }>
<input
type='text'
onChange={ this.updateSearch }
value={ search }
placeholder='Search'
/>
</form>
)
}
}
export default graphql(gql`
{
search(query: "Manchester", type: TEAM) {
name
}
}`)(Search)
You'll want to split this up into at least two components. One that holds the state of what the user searched, then another that actually does the querying by getting a prop. Additionally you can have the apollo higher order component skip the query if the form was submitted without entering something.
import React, {Component} from 'react'
import {graphql} from 'react-apollo'
import gql from 'graphql-tag'
class Results extends Component {
render() {
// apollo provides results under the data prop
const {data} = this.props;
return <h1>{data.search.namej}</h1>
}
}
const ResultsWithQuery = graphql(gql`
query FindTeam($query: String!) {
search(query: $query, type: TEAM) {
name
}
}
`, {skip: (ownProps) => !ownProps.query})(Results);
export class Search extends Component {
constructor(props) {
super(props)
this.state = {
search: ''
}
}
updateSearch = (e) => {
this.setState({
search: e.target.value
})
}
submitSearch = (e) => {
e.preventDefault()
console.log(this.state)
}
render() {
const {search} = this.state;
return (
<div>
<form onSubmit={this.submitSearch}>
<input
type='text'
onChange={this.updateSearch}
value={search}
placeholder='Search'
/>
<ResultsWithQuery query={search} />
</form>
</div>
)
}
}
* UPDATE *
Now that react-apollo#2.1 has been released there is an alternative way using render props.
https://www.apollographql.com/docs/react/essentials/get-started.html#request
This simplifies the number of components you need in this case.
import React, { Component} from 'react'
import { Query } from 'react-apollo'
import gql from 'graphql-tag'
const SearchQuery = gql`
query FindTeam($query: String!) {
search(query: $query, type: TEAM) {
name
}
}
`;
export default class Search extends Component {
constructor(props) {
super(props)
this.state = {
search: ''
}
}
updateSearch = (e) => {
this.setState({
search: e.target.value
})
}
submitSearch = (e) => {
e.preventDefault()
console.log(this.state)
}
render() {
const { search } = this.state;
return (
<form onSubmit={ this.submitSearch }>
<input
type='text'
onChange={ this.updateSearch }
value={ search }
placeholder='Search'
/>
<Query query={SearchQuery} skip={!search} variables={{query: search}}>
{({loading, error, data}) => {
if (loading) return null;
if (error) throw err;
return <h1>{data.search.namej}</h1>
}}
</Query>
</form>
)
}
}
I have created a search box inside component. So I am calling onchange function outside in parent App.js. Now I am trying to dispatch that function if I type anything in search box but I can't access that function outside my class.
How to dispatch my function?
Please find my source code below:
import React, {Component} from "react";
import {connect} from "react-redux";
import { User } from "../components/User";
import { Main } from "../components/Main";
import Data from "../components/Data";
import MovieListing from '../components/MovieListing';
import Header from '../components/Header'
import { setName, getApiData } from "../actions/userActions";
import {apiFetch} from "../actions/dataActions"
import {searchFetch} from "../actions/searchActions"
class App extends Component {
constructor(props){
super(props)
this.searchQuery = this.searchQuery.bind(this);
}
searchQuery( query ) {
}
render() {
let dataSet=this.props.data.data.results;
let imagePath = []
let original_title = []
let release_date = []
let original_language = []
if(dataSet){
dataSet.forEach(function (value, key) {
imagePath.push(<Data key={key} imagePath={value.backdrop_path} release_date={value.release_date} original_title={value.original_title} original_language={value.original_language} />)
original_title.push(value.original_title)
})
return(
<div className="wrapper">
<Header searchQuery = { this.searchQuery } />
<div className="movies-listing">
<div className="container">
<MovieListing imagePath={imagePath} release_date={release_date} original_title={original_title} original_language={original_language} />
</div>
</div>
</div>
)
}else{
return(
<div className="middle-loader">
<h1>Loading</h1>
</div>
)
}
// console.log("this.props",this.props);
}
}
const mapStateToProps = (state) => {
return {
user: state.user,
math: state.math,
data: state.data,
searchData: state.searchData
};
};
const mapDispatchToProps = (dispatch) => {
return dispatch(apiFetch()), {searchQuery: (query) => {searchFetch(query)}}
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
Here I can't access that {searchQuery: (query) => {searchFetch(query)}} because of not accessible that function outside class.
HELP WOULD BE APPRECIATED!!
mapDispatchToProps takes/passes the dispatch function and then return searchQuery function as a prop.
const mapDispatchToProps = (dispatch) => {
return {
searchQuery: (query) => { dispatch(searchFetch(query)) }
}
};
Then in the Header component pass the searchQuery prop
<Header searchQuery={ this.props.searchQuery } />