反应本机钩子,useRef和useEffect

时间:2019-11-11 18:45:11

标签: reactjs react-native react-hooks use-effect

我正在尝试使用React Native将 Class组件转换为 Function

我的Search组件允许用户搜索电影名称,而我正在进行API调用以向他显示所有对应的电影。

这是我的课堂内容:

class Search extends React.Component {

    constructor(props) {
        super(props);
        this.searchedText = "";
        this.page = 0
        this.totalPages = 0
        this.state = {
            films: [],
            isLoading: false
        }
    }

    _loadFilms() {
        if (this.searchedText.length > 0) {
            this.setState({ isLoading: true })
            getFilmsFromApiWithSearchedText(this.searchedText, this.page+1).then(data => {
                this.page = data.page
                this.totalPages = data.total_pages
                this.setState({
                    films: [ ...this.state.films, ...data.results ],
                    isLoading: false
                })
            })
        }
    }

    _searchTextInputChanged(text) {
        this.searchedText = text
    }

    _searchFilms() {
        this.page = 0
        this.totalPages = 0
        this.setState({
            films: [],
        }, () => {
            this._loadFilms()
        })
    }

render() {
        return (
            <View style={styles.main_container}>
                <TextInput
                    style={styles.textinput}
                    placeholder='Titre du film'
                    onChangeText={(text) => this._searchTextInputChanged(text)}
                    onSubmitEditing={() => this._searchFilms()}
                />
                <Button title='Rechercher' onPress={() => this._searchFilms()}/>
                <FlatList
                    data={this.state.films}
                    keyExtractor={(item) => item.id.toString()}
                    renderItem={({item}) => <FilmItem film={item}/>}
                    onEndReachedThreshold={0.5}
                    onEndReached={() => {
                        if (this.page < this.totalPages) {
                            this._loadFilms()
                        }
                    }}
                />
            </View>
        )
    }
}
render() {
      return (
            <View>
                <TextInput
                    onChangeText={(text) => this._searchTextInputChanged(text)}
                    onSubmitEditing={() => this._searchFilms()}
                />
                <Button title='Search' onPress={() => this._searchFilms()}/>
                <FlatList
                    data={this.state.films}
                    keyExtractor={(item) => item.id.toString()}
                    renderItem={({item}) => <FilmItem film={item}/>}
                    onEndReachedThreshold={0.5}
                    onEndReached={() => {
                        if (this.page < this.totalPages) {
                            this._loadFilms()
                        }
                    }}
                />
                {this._displayLoading()}
            </View>
        )
    }
}

如何用钩子翻译以下内容:

  • this.pagethis.totalPages?是useRef解决方案吗?

  • _searchFilms()中,
  • 我正在使用setState回调在影片列表为空时进行新的API调用(因为这是一个新搜索)。但是之后立即执行此操作无效,因为setState是异步的。 但是我找不到用钩子做到这一点的方法。

我认为useEffect可以做到,但是

  • 我只想在我的film列表为空时调用此API,因为我调用_searchFilms()进行新搜索
  • _loadFilms()在用户滚动时被调用,以将更多电影添加到FlatList(用于相同的搜索),因此在这种情况下我无法清除this.films

到目前为止,这是我的翻译方式:

const Search = () => {
    const [searchText, setSearchText] = useState('');
    const [films, setFilms] = useState([]);
    // handle pagination
    const page = useRef(0);
    const totalPages = useRef(0);
    // handle api fetch
    const [isLoading, setIsLoading] = useState(false);


    const loadFilmsFromApi = () => {
        getFilmsFromApiWithSearchedText(searchText, page + 1).then((data) => {
            page.current = data.page;
            totalPages.current = data.total_pages;
            setFilms(films => [...films, ...data.results]);
            setIsLoading(false);
        })
    };

    const searchFilm = () => {
        if (searchText.length > 0) {
            page.current = 0;
            totalPages.current = 0;
            setFilms([]);
            // HERE MY Films list won't be cleared (setState asynchronous)
            loadFilmsFromApi();

            // after the api call, clear input
            setSearchText('');
        }
    };

    useEffect(() => {
        console.log(page, totalPages, "Film number" + films.length);
    }, [films]);

2 个答案:

答案 0 :(得分:1)

我认为您在正确的道路上。对于SELECT CASE WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('productversion')) like '8%' THEN 'SQL2000' WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('productversion')) like '9%' THEN 'SQL2005' WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('productversion')) like '10.0%' THEN 'SQL2008' WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('productversion')) like '10.5%' THEN 'SQL2008 R2' WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('productversion')) like '11%' THEN 'SQL2012' WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('productversion')) like '12%' THEN 'SQL2014' WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('productversion')) like '13%' THEN 'SQL2016' WHEN CONVERT(VARCHAR(128), SERVERPROPERTY ('productversion')) like '14%' THEN 'SQL2017' ELSE 'unknown' END AS MajorVersion, SERVERPROPERTY('ProductLevel') AS ProductLevel, SERVERPROPERTY('Edition') AS Edition, SERVERPROPERTY('ProductVersion') AS ProductVersion totalPages,如果要在不同渲染器之间保持该值(设置状态时),则将其设为page

ref
const Search = () => {

  const [searchText, setSearchText] = useState('');
  const [films, setFilms] = useState([]);
  // handle pagination
  const page = useRef(0);
  const totalPages = useRef(0);
  // handle api fetch
  const [isLoading, setIsLoading] = useState(false);
  
  // This can be invoked by either search or user scroll
  // When pageNum is undefined, it means it is triggered by search
  const loadFilmsFromApi = (pageNum) => {
    console.log("APPEL", 'loadFills');
    getFilmsFromApiWithSearchedText(searchText, pageNum ? pageNum + 1 : 1).then((data) => {
      page.current = data.page;
      totalPages.current = data.total_pages;
      
      setFilms(films => {
        if(pageNum) {
          return [...films, ...data.results];
        } else {
          return [data.results];
        }
      });
      
      setIsLoading(false);
    })
  };
  
  useEffect(() => {
   if (searchText.length > 0) {
      page.current = 0;
      totalPages.current = 0;
      
      setFilms([]);
      loadFilmsFromApi();
      // after the api call, clear input
      setSearchText('');
    }
  
  }, [searchText, loadFilmsFromApi]);


  useEffect(() => {
    console.log(page, totalPages, "Nombre de film " + films.length);
  }, [films]);

  return ( <
    div > Search < /div>
  );

};

答案 1 :(得分:0)

不能完全清楚您的问题是什么,但是听起来您想先清除影片状态,然后再向api发起查询?我在这里也不清楚useRef的用法-useRef只是获得对元素的引用的一种方法,因此以后可以轻松访问它-就像获得对div的引用并能够轻松访问它一样通过myDivRef.current

const = myDivRef = useRef;
...
<div ref={myDivRef}/>

如果是这种情况,那么我只需在API调用的返回中设置一次电影的状态即可。 WRT到引用,似乎您应该只是普通变量或函数中的可能状态项。

更新: 在这里确定了目标之后,您只需在loadFilmsFromApi中添加一个参数即可确定是应该追加还是覆盖:

const loadFilmsFromApi = (append) => {
    getFilmsFromApiWithSearchedText(searchText, page + 1).then((data) => {
        page.current = data.page;
        totalPages.current = data.total_pages;

        if (append) {
            setFilms({
                films: this.state.films.concat(data.results)
            });
        } else {
            setFilms({
                films: data.results
            });
        }

        setIsLoading(false);
    })
};