简体   繁体   English

React Native 钩子,useRef 和 useEffect

[英]React Native hooks, useRef and useEffect

I'm trying to translate a Class Component into a Functional one with React Native.我正在尝试将Class 组件转换为具有 React Native 的功能性组件。

My Search component lets the user search for a film name and I'm making an API call to show him all corresponding films.我的Search组件让用户搜索电影名称,我正在调用 API 向他展示所有相应的电影。

Here is my class component:这是我的 class 组件:

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

How can I translate the following with hooks:如何使用钩子翻译以下内容:

  • this.page and this.totalPages ? this.pagethis.totalPages is useRef the solution? useRef 是解决方案吗?

  • in _searchFilms() I'm using setState callback to make a new API call when my film list is empty (because it's a new search)._searchFilms()中,当我的电影列表为空时(因为这是一个新搜索),我正在使用setState回调进行新的 API 调用。 But doing it right after doesn't work because setState is asynchronous.但是因为setState是异步的,所以在之后立即执行它是行不通的。 But I can't find a way to do it with hooks.但我找不到用钩子做的方法。

I think useEffect could do this but:我认为useEffect可以做到这一点,但是:

  • I only want to make this API call when my film list is empty, because I call _searchFilms() for a new search .我只想在我的film列表为空时进行此 API 调用,因为我调用_searchFilms()进行新搜索
  • _loadFilms() is called on user scroll to add more films to the FlatList (for the same search) so I can't clear this.films in this case. _loadFilms()在用户滚动时被调用以向 FlatList 添加更多电影(用于相同的搜索),因此在这种情况下我无法清除this.films

Here is how I translated it so far:到目前为止,我是这样翻译的:

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]);

I think you are on the right path.我认为你走在正确的道路上。 As for totalPages and page , having it as a ref makes sense if you want to maintain that values between different renders ( when setting state )至于totalPagespage ,如果您想在不同的渲染之间保持该值(设置 state 时),将其作为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> ); };
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Not totally clear what your question is, but it sounds like you want to clear films state before you fire off the query to the api?不完全清楚你的问题是什么,但听起来你想在向 api 发起查询之前清除电影 state? I am also not clear on the use of useRef here - useRef is simply a way to get a reference to an element so it's easy to access it later - like get a reference to a div and be able to access it easily via myDivRef.current我也不清楚 useRef 在这里的使用 - useRef只是一种获取元素引用的方法,因此以后很容易访问它 - 比如获取对 div 的引用并能够通过myDivRef.current轻松访问它

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

If that is the case, then I would simply set the state of films once in the return of the API call.如果是这种情况,那么我只需在 API 调用的返回中设置一次电影的 state。 WRT to the refs, it seems like you this should just be normal variables, or possible state items in your function. WRT 给裁判,看起来你这应该只是普通变量,或者你的 function 中可能的 state 项目。

UPDATE : After clearing up the goal here, you could simply add a parameter to loadFilmsFromApi to determine if you should append or overwrite:更新:在这里清除目标后,您可以简单地向loadFilmsFromApi添加一个参数以确定您是否应该 append 或覆盖:

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);
    })
};

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

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