1

I am trying to have a controlled input set up in a child component (the Search component). I wanted to keep the input state in the main App component so that I can access it in my apiCall method. I am getting the following error:

Warning: You provided a value prop to a form field without an onChange handler. This will render a read-only field. If the field should be mutable use defaultValue. Otherwise, set either onChange or readOnly.

However, I did add an onChange handler. I'm assuming the problem is that the onChange handler function is in the parent component and React doesn't like this. I did try moving the input to the main App component and worked fine (logged input to console).

Am I going about this wrong? And is there a way to set it up so that I can access the input from the Search component in the App component? I was hoping to keep most of my code/functions/state in the main App component.

Here is the App component

import React, { Component } from "react";
import './App.css';
import Header from './Components/Header'
import Search from './Components/Search'
import MainInfo from './Components/MainInfo'
import Details from './Components/Details'

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      weather: null,
      main: '',
      wind: '',
      loading: null,
      cityInput: 'Houston',
      city: 'City Name',
      date: new Date()
    };
    this.apiCall = this.apiCall.bind(this);
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(event) {
    this.setState({
      cityInput: event.target.value
    })
    console.log(this.state.cityInput)
  }

  // Fetch data from OpenWeatherAPI
  apiCall() {
    this.setState({
      loading: true
    })

    const currentWeather = fetch(
      `https://api.openweathermap.org/data/2.5/weather?q=${this.state.cityInput}&appid={apiKey}&units=imperial`
    ).then((res) => res.json());

    const futureWeather = fetch(
      `https://api.openweathermap.org/data/2.5/forecast?q=houston&appid={apiKey}&units=imperial`
    ).then((res) => res.json());

    const allData = Promise.all([currentWeather, futureWeather]);

    // attach then() handler to the allData Promise
    allData.then((res) => {
      this.setState({
        weather: res[0].weather,
        main: res[0].main,
        wind: res[0].wind,
        city: res[0].name
      })
    });
  }

  componentDidMount() {
    this.apiCall();
  }

  render() {
    return (
      <div className="container-fluid bg-primary vh-100 vw-100 d-flex flex-column align-items-center justify-content-around p-3">
        <Header />
        <Search cityInput={this.state.cityInput} />
        <MainInfo main={this.state.main} date={this.state.date} city={this.state.city} weather={this.state.weather} />
        <Details main={this.state.main} wind={this.state.wind} />
      </div>
    );
  }
}

export default App;


Here is Search component

import React, { Component } from "react";

class Search extends Component {
    constructor(props) {
        super(props);
    }
    render() {
        return (
            <div className="row">
                <div className="col-12">
                    <div className="d-flex">
                        <input className="form-control shadow-none mx-1" placeholder="Enter a city..." value={this.props.cityInput} onChange={this.handleChange}></input>
                        <button className="btn btn-light shadow-none mx-1" onClick={this.apiCall}>Test</button></div>
                </div>
            </div>
        );
    }
}

export default Search;

2 Answers 2

2

The Search component is indeed unaware of the implementation of the onChange function you have made in your App. If you really want to use a function from the parent (App) component in the child (Search), you'll need to add it as a property, as such:

<Search cityInput={this.state.cityInput} onChange={this.onChange} />

Then, you need to set it in the Child component's constructor:

constructor(props) {
    super(props);
    this.onChange = props.onChange;
}

I also suggest you'll have a look at React's functional approach with hooks https://reactjs.org/docs/hooks-intro.html, which makes all this a whole lot less fiddly, in my opinion. But it might take a bit to get used to.

Sign up to request clarification or add additional context in comments.

1 Comment

Functional approach, and passing onChange as a prop is what I've done before too. With functional react, using the useRef() hook is helpful for accessing the value of an input that changes.
0

u can pass functions like ur handler over the prop to childrens and update so from a child to the mother of the children, in the children u give the value the prop u supply from mother

<Select dataFeld="broker" id="yourid" value={this.state.brokerSel}   ownonChange={(e) => this.setState({statename: e})

1 Comment

np problem, this also work with functions u want to call from the mother in children u should take only a look about theists is not fired without u call it (like when u put it on a button) U also should look about arrow functions like = () => so u have not to bind the stuff. Would be nice to get a thumb up :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.