简体   繁体   English

重构类以在 React 中运行组件

[英]Refactor class to function components in React

尝试搜索术语和位置后的结果导致随机结果 I'm having a hard time converting these 3 class components to function components, the class components are working i just am trying to convert them for learning purposes.我很难将这 3 个类组件转换为函数组件,类组件正在工作,我只是想将它们转换为学习目的。

the API call: yelp.js API 调用:yelp.js

const { default: SearchBar } = require("../components/SearchBar/SearchBar");

const Yelp = {
    searchYelp(term, location) {
        return fetch(`/api/hello?term=${term}&location=${location}`)
        .then((response) => {
            // console.log(response)
            return response.json()
        }).then((jsonResponse) => {
            // console.log(jsonResponse)
            if (jsonResponse.businesses) {
                return jsonResponse.businesses.map((business) => {
                    return {
                        id: business.id,
                        imageSrc: business.image_url,
                        name: business.name,
                        address: business.location.address1,
                        city: business.location.city,
                        state: business.location.state,
                        zipCode: business.location.zip_code,
                        category: business.categories.title,
                        rating: business.rating,
                        reviewCount: business.review_count,
                    }
                })
            }
        })
    }
}

export default Yelp

The Home component as a function that renders a SearchBar and BusinessList component: Home.js Home 组件作为呈现 SearchBar 和 BusinessList 组件的函数:Home.js

import React, { useState } from "react";
import BusinessList from '../../../src/components/BusinessList/BusinessList';
import SearchBar from '../../../src/components/SearchBar/SearchBar';
import Yelp from '../../util/yelp';

const Home = (term, location) => {
    const [businesses, setBusinesses] = useState([]);

    const searchYelp = Yelp.searchYelp(term, location).then(businesses => {
        setBusinesses(businesses)
    })

    return (
        <>
            <SearchBar searchYelp={searchYelp} />
            <BusinessList business={businesses} />
        </>
    )
}

export default Home;

The Home component as a class: Home.js Home 组件作为一个类:Home.js

// import React from 'react';
// import BusinessList from '../../../src/components/BusinessList/BusinessList';
// import SearchBar from '../../../src/components/SearchBar/SearchBar';
// import Yelp from '../../util/yelp';

// class Home extends React.Component {
//     constructor() {
//         super();
//         this.state = {
//             businesses: [],
//         };

//         this.searchYelp = this.searchYelp.bind(this);
//     }

//     searchYelp(term, location, sortBy) {
//         Yelp.searchYelp(term, location, sortBy).then((businesses) => {
//             this.setState({ businesses: businesses })
//         })
//     }
//     render() {
//         return (
//             <>
//                 <SearchBar searchYelp={this.searchYelp} />
//                 <BusinessList businesses={this.state.businesses} />
//             </>
//         )
//     }
// }

// export default Home;

The BusinessList component as a function that renders a Business component: BusinessList.js作为呈现业务组件的函数的 BusinessList 组件:BusinessList.js

import React, { useState } from "react";
import './BusinessList.css';
import Business from '../Business/Business';

function BusinessList(businesses) {
    console.log(businesses)
    return (
            <div className="BusinessList">
                {
                businesses.map(business => {
                    <Business key={business.id} business={business} />
                    })
                }
            </div>
    )
};

export default BusinessList;

The BusinessList component as a class: BusinessList.js作为类的 BusinessList 组件:BusinessList.js

// import React from 'react';
// import './BusinessList.css';
// import Business from '../Business/Business';

// class BusinessList extends React.Component {
//     constructor(props) {
//         super(props)
//         console.log(props.businesses)
//     }

//     render() {
//         return (
//             <div className="BusinessList">
//                 {
//                     this.props.businesses.map((business) => {
//                         return <Business key={business.id} business={business} />
//                     })
//                 }
//             </div>
//         )
//     }
// };

// export default BusinessList;

The Business component as a function: Business.js作为函数的业务组件:Business.js

import React from "react";
import './Business.css';

const Business = (business) => {
    return (
        <div className="Business">
            <div className="image-container">
                <img src={business.business.imageSrc} alt={business.imageSrc} />
            </div>
            <h2>{business.business.name}</h2>
            <div className="Business-information">
                <div className="Business-address">
                    <p>{business.business.address}</p>
                    <p>{business.business.city}&nbsp;{business.state}&nbsp;{business.zipCode}</p>
                </div>
                <div className="Business-reviews">
                    <h3>{business.business.category}</h3>
                    <h3 className="rating">{business.business.rating}</h3>
                    <p>{business.business.reviewCount} reviews</p>
                </div>
            </div>
        </div>
    )
};

export default Business;

The Business component as a class: Business.js作为一个类的业务组件:Business.js

// import React from "react";
// import './Business.css';

// class Business extends React.Component {
//     render() {
//         const { business } = this.props

//         return (
//             <div className="Business">
//                 <div className="image-container">
//                     <img src={business.imageSrc} alt={business.imageSrc} />
//                 </div>
//                 <h2>{business.name}</h2>
//                 <div className="Business-information">
//                     <div className="Business-address">
//                         <p>{business.address}</p>
//                         <p>{business.city}&nbsp;{business.state}&nbsp;{business.zipCode}</p>
//                     </div>
//                     <div className="Business-reviews">
//                         <h3>{business.category}</h3>
//                         <h3 className="rating">{business.rating}</h3>
//                         <p>{business.reviewCount} reviews</p>
//                     </div>
//                 </div>
//             </div>
//         )
//     }
// };

// export default Business;

EDIT ** My attempt at SearchBar component as function: SearchBar.js编辑 ** 我尝试将 SearchBar 组件作为函数:SearchBar.js

import React, { useState, useEffect } from "react";
import './SearchBar.css';

const SearchBar = (props) => {
    const [term, setTerm] = useState('')
    const [location, setLocation] = useState('')
    const [sortBy, setSortBy] = useState('best_match')

    const sortByOptions = {
        'Best Match': 'best_match',
        'Highest Rated': 'rating',
        'Most Reviewed': 'review_count'
    };

    const handleSortByChange = () => {
        setSortBy(sortBy)
        // console.log(sortByOption)
        console.log(sortBy)
    }

    const renderSortByOptions = (sortByOptions) => {
        // console.log(Object.keys(sortByOptions))
        return Object.keys(sortByOptions).map(sortByOption => {
            let sortByOptionValue = sortByOptions[sortByOption]
            // console.log(sortByOptionValue)
            return <li
                className={sortBy === sortByOption ? 'active' : ''}
                onClick={handleSortByChange}
                key={sortByOptionValue}>
                {sortByOption}
            </li>;
        })
    }

    const handleTermChange = (event) => {
        setTerm(event.target.value)
    }

    const handleLocationChange = (event) => {
        setLocation(event.target.value)
    }

    const handleSearch = (event) => {
        event.preventDefault()
        props.searchYelp(term, location)
    }

    return (
        <div className="SearchBar">
            {props.searchYelp}
            <div className="SearchBar-sort-options">
                <ul>
                    {renderSortByOptions(sortByOptions)}
                </ul>
            </div>
            <div className="SearchBar-fields">
                <input
                    onChange={handleTermChange}
                    placeholder="Search Businesses"
                />
                <input
                    onChange={handleLocationChange}
                    placeholder="Where?"
                />
                <button className="SearchBar-submit" onClick={handleSearch}>Let's Go</button>
            </div>
        </div>
    )
}

export default SearchBar;

EDIT** SearchBar component as a class: SearchBar.js编辑** SearchBar 组件作为一个类:SearchBar.js

import React from 'react';
import './SearchBar.css';

class SearchBar extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            term: '',
            location: '',
            sortBy: 'best_match'
        }

        this.handleTermChange = this.handleTermChange.bind(this)
        this.handleLocationChange = this.handleLocationChange.bind(this)
        this.handleSearch = this.handleSearch.bind(this)

        this.sortByOptions = {
            'Best Match': 'best_match',
            'Highest Rated': 'rating',
            'Most Reviewed': 'review_count'
        };
    }

    getSortByClass(sortByOption) {
        // console.log(sortByOption)
        if (this.state.sortBy === sortByOption) {
            return 'active'
        }
        return ''
    }

    handleSortByChange(sortByOption) {
        this.setState({
            sortBy: sortByOption
        })
    }

    handleTermChange(event) {
        this.setState({
            term: event.target.value
        })
    }

    handleLocationChange(event) {
        this.setState({
            location: event.target.value
        })
    }

    handleSearch(event) {
        this.props.searchYelp(this.state.term, this.state.location, this.state.sortBy)
        event.preventDefault()
    }

    renderSortByOptions() {
        return Object.keys(this.sortByOptions).map(sortByOption => {
            let sortByOptionValue = this.sortByOptions[sortByOption]
            console.log(sortByOptionValue)
            return <li
                onClick={this.handleSortByChange.bind(this, sortByOptionValue)}
                className={this.getSortByClass(sortByOptionValue)}
                key={sortByOptionValue}>
                {sortByOption}
            </li>;
        })
    }

    render() {
        return (
            <div className="SearchBar">
                {this.searchYelp}
                <div className="SearchBar-sort-options">
                    <ul>
                        {this.renderSortByOptions()}
                    </ul>
                </div>
                <div className="SearchBar-fields">
                    <input onChange={this.handleTermChange} placeholder="Search Businesses" />
                    <input onChange={this.handleLocationChange} placeholder="Where?" />
                    <button className="SearchBar-submit" onClick={this.handleSearch}>Let's Go</button>
                </div>
            </div>
        )
    }
};

export default SearchBar;

I keep getting the error "Cannot read properties of undefined (reading 'map') Or the error "Businesses.map is not a function"我不断收到错误“无法读取未定义的属性(读取‘地图’)或错误“Businesses.map 不是函数”

Im also a little confused as to why when everything is converted to function components in my final Business component in order to get things to showup im required to pass things in as business.business.imageSrc instead of just business.imageSrc我也有点困惑,为什么在我的最终业务组件中将所有内容都转换为功能组件以显示内容时,我需要将内容作为 business.business.imageSrc 传递,而不仅仅是 business.imageSrc

BusinessList receives props , an object containing the props passed in. BusinessList接收props ,一个包含传入的 props 的对象。

The function parameter would either need to destructure it:函数参数要么需要解构它:

function BusinessList({ businesses }) { ... }

Or reference it off the props object:或者从props对象引用它:

function BusinessList(props) {
  console.log(props.businesses)
  // ...
}

First in Home searchYelp should be declared as a function so it can be passed as a callback to the SearchBar component.首先在Home searchYelp应该声明为一个函数,以便它可以作为回调传递给SearchBar组件。

const Home = () => {
  const [businesses, setBusinesses] = useState([]);

  const searchYelp = (term, location) => {
    Yelp.searchYelp(term, location)
      .then(businesses => {
        setBusinesses(businesses);
      });
  };

  return (
    <>
      <SearchBar searchYelp={searchYelp} />
      <BusinessList business={businesses} />
    </>
  )
};

Then in BusinessList you need to access the passed business prop.然后在BusinessList您需要访问传递的business道具。 Your current code is naming the props object businesses and then attempts to map it.您当前的代码正在命名道具对象businesses ,然后尝试映射它。 It could be businesses.business.map , but by convention we name the props object props or simply destructure the props you want to use.它可以是businesses.business.map ,但按照惯例,我们将 props 对象命名为props或简单地解构您想要使用的 props。 You need to also return the Business component you are mapping to.您还需要返回您要映射到的Business组件。

function BusinessList({ business }) {
  return (
    <div className="BusinessList">
      {business.map(business => {
        return <Business key={business.id} business={business} />;
      })}
    </div>
  )
};

Same issue with the props object name in the Business component.Business组件中的道具对象名称相同的问题。

const Business = (props) => {
  return (
    <div className="Business">
      <div className="image-container">
        <img src={props.business.imageSrc} alt={props.business.imageSrc} />
      </div>
      <h2>{props.business.name}</h2>
      <div className="Business-information">
        <div className="Business-address">
          <p>{props.business.address}</p>
          <p>{props.business.city}&nbsp;{props.business.state}&nbsp;{business.zipCode}</p>
        </div>
        <div className="Business-reviews">
          <h3>{props.business.category}</h3>
          <h3 className="rating">{props.business.rating}</h3>
          <p>{props.business.reviewCount} reviews</p>
        </div>
      </div>
    </div>
  )
};

Few notes:几点注意事项:

  • Right now Yelp.searchYelp returns Promise<any[] | undefined>现在Yelp.searchYelp返回Promise<any[] | undefined> Promise<any[] | undefined> , ie undefined is a legitimate value that the consume may get. Promise<any[] | undefined> ,即undefined是消费可能获得的合法值。 Up to you to decide if setBusinesses(businesses) when businesses is undefined is useful or not, but in that case, handle it.由您决定当businesses未定义时setBusinesses(businesses)是否有用,但在这种情况下,请处理它。 Otherwise default to an empty array, setBusinesses(businesses ?? []) or throw an error.否则默认为空数组, setBusinesses(businesses ?? [])或抛出错误。
  • Do not run side effects in the render phase, ie call the api inside a useEffect :不要在渲染阶段运行副作用,即在useEffect调用 api:
React.useEffect(() => {
    const searchYelp = Yelp.searchYelp(term, location).then(businesses => {
        setBusinesses(businesses ?? [])
    })
}, [term, location])
  • Lastly, const Business = (business) => { here business is actually the props object.最后, const Business = (business) => {这里business实际上是 props 对象。 You can simply destructure it const Business = ({ business }) => { to get the value directly.您可以简单地对其进行解构const Business = ({ business }) => {以直接获取值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM