如何在不使用Redux的情况下将变量值从子级发送到父级

时间:2020-08-14 12:17:24

标签: javascript reactjs

我有一个Blog组件,其中有一个Search组件。我需要有权访问我的Blog组件中的searchResults变量。如何将其从搜索组件传递到博客组件?

这是父项(博客组件):

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import Pagination from "react-pagination-js";
import Spinner from '../Spinner/Spinner';
import { Link } from 'react-router-dom';
import Footer from '../Footer/Footer.jsx';
import CustomHeader from '../CustomHeader/CustomHeader.jsx';
import Search from '../Search/Search.jsx';



const Blog = () => {

  let title = 'Articles'

  let [posts, setPosts] = useState([]);
  let [isMounted] = useState(false)
  let [currentPage, setCurrentPage] = useState(1);
  let [loading, setLoading] = useState(false);
  let [isVisible] = useState(true);
  const [postsPerPage] = useState(5);
  const GET_POSTS_API = process.env.REACT_APP_GET_POSTS_API;



  useEffect(() => {
    const fetchPosts = async () => {
      isMounted = true;
      setLoading(true);
      if (isMounted) {
        let res = await axios.get(GET_POSTS_API);
        setPosts(res.data);
      }
      setLoading(false);
    };

    fetchPosts();
  }, []);

  isMounted = false
  // Get current posts
  const indexOfLastPost = currentPage * postsPerPage;
  const indexOfFirstPost = indexOfLastPost - postsPerPage;
  const currentPosts = posts.slice(indexOfFirstPost, indexOfLastPost);
  let totalPagesGenerated = posts.length / postsPerPage;
  let totalPagesGeneratedCeiled = Math.ceil(totalPagesGenerated);
  if (loading) {
    return <Spinner />
  }

  // Change page
  const paginate = (pageNumber) => {
    Math.ceil(totalPagesGenerated)
    if (pageNumber > 0 && pageNumber <= totalPagesGeneratedCeiled) {
      setCurrentPage(pageNumber);
    }
  }

  return (
    <div>
      <CustomHeader
        title={title}
      />
      <Search />
      <div className="row">
        <div className="column">
          {currentPosts.map(post => (
            <div key={post._id} className='post'>
              <img className="post-container__image" src={post.picture} alt="avatar" />
              <div className="post-container__post">
                <div className="post-container__text">
                  <h2 className="post-container__title">{post.title}</h2>
                  <p className="post-container__date">{post.date}</p>
                  <p className="post-info-container__text">{post.postContent.substr(0, 310) + "..."}</p>
                  <Link to={`/post/${post._id}`} className="read-more-btn">
                    <button className="read-more-btn">Read more</button>
                  </Link>
                </div>
              </div>
            </div>
          ))}
          <Pagination
            currentPage={currentPage}
            currentPosts={currentPosts}
            showFirstLastPages={true}
            sizePerPage={postsPerPage}
            totalSize={posts.length}
            totalPages={posts.length}
            changeCurrentPage={paginate}
          />
        </div>
      </div>
      <Footer />
    </div>
  );
};

export default Blog;

这是子级(搜索组件):

import React, { Component } from "react";
import Spinner from '../../components/Spinner/Spinner.jsx';
import { Link } from 'react-router-dom';
import axios from "axios";

class Search extends Component {
    constructor(props) {
        super(props);

        this.state = {
            searchResults: [],
            isLoading: false,
            isSearchStringFound: true,
            placeholder: ''
        }
    }

    handleSearchQuery = (event) => {
        const SEARCH_RESULTS_ENDPOINT = process.env.REACT_APP_SEARCH_ENDPOINT;
        let searchString = document.querySelector(".search-input").value;

        if (event.keyCode === 13) {
            this.setState({ ...this.state, isLoading: true });
            axios.post(SEARCH_RESULTS_ENDPOINT, {
                searchString: searchString,

            }).then(response => {
                this.setState({ ...this.state, searchResults: response.data });

                if (response.data.length === 0) {
                    this.setState({ ...this.state, isSearchStringFound: false });
                }

                else if (response.data.length > 0) {
                    this.setState({ ...this.state, isSearchStringFound: true });
                }

                this.setState({ ...this.state, isLoading: false });
            });

            this.setState({ ...this.state, placeholder: searchString });
        }
    };


    render() {

        if (this.state.isLoading) {
            return <Spinner />
        }
        return (

            <div>
                <div>
                    <input
                        type="text"
                        placeholder={this.state.placeholder}
                        className="search-input"
                        onKeyDown={(e) => this.handleSearchQuery(e)}
                    />

                    <div className="results-container">
                        <div>
                            {this.state.isSearchStringFound === false ? <div className="no-results-found">No results were found</div> : this.state.searchResults.map(result => (
                                <div key={result._id} className="results-box" >
                                    <img src={result.picture} alt="avatar" className="results-container-img" />
                                    <div className="results-box-body">
                                        <div>
                                            <h2>{result.title.toUpperCase()}</h2>
                                            <p>{result.postContent.substr(0, 310) + "..."}</p>
                                            <p>{result.date}</p>
                                            <Link to={`/post/${result._id}`} className="read-more-btn">
                                                <button className="read-more-btn">Read more</button>
                                            </Link>
                                        </div>
                                    </div>
                                </div>
                            ))}
                        </div>
                    </div>
                </div>
            </div>
        );
    }
}

export default Search;

是否可以在不使用Redux的情况下执行此操作?

3 个答案:

答案 0 :(得分:0)

子组件应该从父类中获取一个回调属性。有点像按钮的工作方式:

<Button onClick={this.onButtonClick}

您要做的是

<SearchComponent onSearchResults={this.onResults}

然后,在搜索组件中,您可以调用this.props.onSearchResults(searchResults);

答案 1 :(得分:0)

您可以使用回调函数来做到这一点。

基本上,您将一个函数传递给子组件,子组件将触发该函数,而父组件将具有该值。

以下是应如何实施的简单示例:

父母:

decimal(10,2)

孩子:

const Parent = () => {
  const onSearchResult = searchResults => {
    console.log(searchResults)
 }

  return (
    <>
      I am the parent component
      <Child onSearchResult={onSearchResult} />
    </>
  )
}

答案 2 :(得分:0)

我更喜欢两种方法将变量传递给子组件。它们分别在不同情况下很有用

方法1:使用属性=>道具

如果您的组件树未深度嵌套,则此方法很有用。例如,您希望将变量从父级传递给子级。

嵌套组件如下

const ParentComponent = () => {
 
     const [variable, setVariable] = useState(0);

     return (
         <ChildComponent variable={variable} setVariable={setVariable} /> //nested within ParentComponent, first level
      )
}

const ChildComponent = (props) => {
    
      return(
          <>
              <div>prop value is {props.variable}</div> //variable attribute available through component props
              <button onClick={props.setVariable(prevValue => prevValue+1}>add +1</button> //set value in parent through callBack method
          </>
       )

}

如果您具有高度嵌套的组件层次结构,则情况会有些混乱。可以说,ChildComponent返回了另一个组件,并且您希望将variable传递到该组件,但是ChildComponent不需要该变量,您最终会遇到这种情况

const ParentComponent = () => {
 
     const [variable, setVariable] = useState(false);

     return (
         <ChildComponent someProp={variable}/> //nested within ParentComponent, first level
      )
}

const ChildComponent = (props) => {
    
      return(
          <AnotherCustomComponent someProp={props.someProps}/> //someProp attribute available through component props
       )

}


const AnotherCustomComponent = (props) => {
    
      return(
          <div>prop value is {props.someProp}</div> //someProp attribute available through component props
       )

}

即使ChildComponent不需要该道具,它也需要通过道具将其推入其子组件。这就是所谓的“钻探”。这是一个简单的示例,但是对于更复杂的系统,它可能会变得非常混乱。为此,我们使用...

方法2:上下文API CodeSandbox

Context API提供程序提供了一种向子组件提供状态的灵巧方法,而不会导致最终钻探情况。它要求设置Provider,并将其值提供给可以使用上下文的任何Consumers'. Any component that is a child of the Provider`。

首先创建一个上下文。

CustomContext.js

import React from 'react';

const CustomContext = React.createContext();

export function useCustomContext() {
  return React.useContext(CustomContext);
}

export default CustomContext;

下一步是实现提供程序,并为其提供值。我们可以使用较早版本的ParentComponent并添加Context提供程序

import CustomContext from './CustomContext'

const ParentComponent = () => {

  const [variable, setVariable] = useState(false);

  const providerState = {
    variable,
    setVariable
  }

  return (
    <CustomContext.Provider value={providerState} >
      <ChildComponent />
    </CustomContext.Provider>
  )
}

现在,嵌套在 中的任何组件都可以访问传递到Provider

的“值”属性中的任何内容

我们嵌套的子组件看起来像这样

const ChildComponent = (props) => {

  return(
      <AnotherCustomComponent/> //Notice we arent passing the prop here anymore
   )

}


const AnotherCustomComponent = (props) => {

  const {variable, setVariable} = useCustomContext(); //This will get the value of the "value" prop we gave to the provider at the parent level

  return(
      <div>prop value is {variable}</div> //variable pulled from Context
   )

}

如果两次使用ParentComponent,则ParentComponent的每个实例将为其子级提供其自己的“ CustomContext”。

const App() {

    return (
        <>
            <ParentComponent/> 
            <ParentComponent/>
        </>    

}