Home > Mobile >  React.js Controlled Input in child component
React.js Controlled Input in child component

Time:10-17

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;

Edit: This was a private repo and I forgot that I had my API keys in here! I will be renewing my API keys so these are no longer valid.

CodePudding user response:

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.

CodePudding user response:

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})
  • Related