【问题标题】:Converting React Class component to Function Component With Hook使用 Hook 将 React 类组件转换为函数组件
【发布时间】:2020-07-10 01:51:04
【问题描述】:

我尝试在下面的反应应用程序中转换类组件:

import React, { Component } from 'react'
import ReactTable from 'react-table'
import api from '../api'

import styled from 'styled-components'

import 'react-table/react-table.css'

const Wrapper = styled.div`
    padding: 0 40px 40px 40px;
`

const Update = styled.div`  
    color: #ef9b0f;
    cursor: pointer;
`

const Delete = styled.div`
    color: #ff0000;
    cursor: pointer;
`

class UpdateVoter extends Component {
    updateUser = event => {
        event.preventDefault()

        window.location.href = `/voters/update/${this.props.id}`
    }

    render() {
        return <Update onClick={this.updateUser}>Update</Update>
    }
}

class DeleteVoter extends Component {
    deleteUser = event => {
        event.preventDefault()

        if (
            window.confirm(
                `Do you want to delete this voter ${this.props.id} permanently?`,
            )
        ) {
            api.deleteVoterById(this.props.id)
            window.location.reload()
        }
    }

    render() {
        return <Delete onClick={this.deleteUser}>Delete</Delete>
    }
}

class VotersList extends Component {
    constructor(props) {
        super(props)
        this.state = {
            voters: [],
            columns: [],
            isLoading: false,
        }
    }

    componentDidMount = async () => {
        this.setState({ isLoading: true })

        await api.getAllVoters().then(voters => {
            this.setState({
                voters: voters.data.data,
                isLoading: false,
            })
        })
    }

    render() {
        const { voters, isLoading } = this.state

        const columns = [
            {
                Header: 'ID',
                accessor: '_id',
                filterable: true,
            },
            {
                Header: 'No KK',
                accessor: 'nkk',
                filterable: true,
            },
            {
                Header: 'NIK',
                accessor: 'nik',
                filterable: true,
            },
            {
                Header: 'Nama',
                accessor: 'nama',
                filterable: true,
            },
            {
                Header: 'Alamat',
                accessor: 'alamat',
                filterable: true,
            },
            {
                Header: '',
                accessor: '',
                Cell: function(props) {
                    return (
                        <span>
                            <DeleteVoter id={props.original._id} />
                        </span>
                    )
                },
            },
            {
                Header: '',
                accessor: '',
                Cell: function(props) {
                    return (
                        <span>
                            <UpdateVoter id={props.original._id} />
                        </span>
                    )
                },
            },
        ]

        let showTable = true
        if (!voters.length) {
            showTable = false
        }

        return (
            <Wrapper>
                {showTable && (
                    <ReactTable
                        data={voters}
                        columns={columns}
                        loading={isLoading}
                        defaultPageSize={10}
                        showPageSizeOptions={true}
                        minRows={0}
                    />
                )}
            </Wrapper>
        )
    }
}

export default VotersList

到带有钩子的功能组件,像这样:

import React, {useState, useEffect} from 'react'
import ReactTable from 'react-table'
import api from '../api'

import styled from 'styled-components'

import 'react-table/react-table.css'

const Wrapper = styled.div`
    padding: 0 40px 40px 40px;
`

const Update = styled.div`
    color: #ef9b0f;
    cursor: pointer;
`

const Delete = styled.div`
    color: #ff0000;
    cursor: pointer;
`

function UpdateVoter(props) {
    const updateUser = event => {
        event.preventDefault()

        window.location.href = `/voters/update/${props.id}`
    }

    
        return <Update onClick={updateUser}>Update</Update>

}

function DeleteVoter(props) {
    const deleteUser = event => {
        event.preventDefault()

        if (
            window.confirm(
                `Do tou want to delete this voter ${props.id} permanently?`,
            )
        ) {
            api.deleteVoterById(props.id)
            window.location.reload()
        }
    }

    
        return <Delete onClick={deleteUser}>Delete</Delete>
  
}

function VotersListSpecific(props) {
    const [state, setState] = useState ({
            voters: [],
            columns: [],
            isLoading: false,
        })
   

    useEffect(() => {
        async function fetchData() {
            setState({ ...state, isLoading: true })

            let voters = await api.getAllVoters()
            setState({
                voters: voters.data.data, 
                ...state,
                isLoading: true,
            })
        }
        fetchData()
        console.log(fetc)
    }, [])

        const { voters, isLoading } = state

        const columns = [
            {
                Header: 'ID',
                accessor: '_id',
            },
            {
                Header: 'No KK',
                accessor: 'nkk',
            },
            {
                Header: 'NIK',
                accessor: 'nik',
            },
            {
                Header: 'Nama',
                accessor: 'nama',
            },
            {
                Header: 'Alamat',
                accessor: 'alamat',
            },
            {
                Header: '',
                accessor: '',
                Cell: function(props) {
                    return (
                        <span>
                            <DeleteVoter id={props.original._id} />
                        </span>
                    )
                },
            },
            {
                Header: '',
                accessor: '',
                Cell: function(props) {
                    return (
                        <span>
                            <UpdateVoter id={props.original._id} />
                        </span>
                    )
                },
            },
        ]

        let showTable = true
        if (!voters.length) {
            showTable = false
        }

        return (
            <Wrapper>
                {showTable && (
                    <ReactTable
                        data={voters}
                        columns={columns}
                        loading={isLoading}
                        defaultPageSize={10}
                        showPageSizeOptions={true}
                        minRows={0}
                    />
                )}
            </Wrapper>
        )
    
}

export default VotersList

但是,代码不起作用。该表不显示。州内的“选民”数组是空的。除此之外,我还收到了这个警告:

React Hook useEffect 缺少一个依赖项:'state'。要么包括 它或删除依赖数组。您还可以进行功能更新 'setState(s => ...)' 如果你在 'setState' 调用中只需要 'state' react-hooks/exhaustive-deps

我真的需要帮助来解决这个问题。提前谢谢你。

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    我的建议

    首先,useState 不像类组件中的this.state。建议您为每个原语 state 分配一个 useState 函数。例如,在您的VotersListSpecific 组件中,您可以拥有:

    const [voters, setVoters] = useState([])
    const [columns, setColumns] = useState([])
    const [isLoading, setLoading] = useState(false)
    

    仍然可以使用useReducer 对其进行进一步优化,但现在它太离题了。有兴趣的可以查看official docs

    你的问题

    现在让我们分析一下你做对了什么,做错了什么。

    权利

    当你改变状态时,你使用语法setState({ ...state, isLoading: true }),这是正确的,因为 React 只在对象的引用改变时切换重新渲染。如果你使用这样的东西:

    state.isLoading = false
    setState(state)
    

    在这种情况下状态没有改变的引用,所以 React 不会重新渲染。

    错误

    调用const { voters, isLoading } = state时,voters变量指向状态的voters字段,第一次渲染时为空数组。一段时间后,当用新的voters 创建新的state 时,新的state.voters 实际上指向一个新数组。但是 React 不知道这个变化,因为你已经明确地将 voter 变量指向了原来的空数组,它是旧的 state 的一个字段。

    对此的解决方法如下:

    const [voters, setVoters] = useState([])
    useEffect(async () => {
      setVoters(await api.getAllVoters())
    }, [])
    return (
      <ReactTable data={voters}/>
    )
    

    另一种方法是使用data={state.voters}

    【讨论】:

    • 对不起。 “新的state.voters 实际上指向一个新数组”是什么意思?我的理解是在第一次渲染之后,状态由useEffect 函数更改,并触发第二次渲染,它用新状态填充voters 变量(状态包含来自api.getAllVoters 函数的数据)。如果我错了,请纠正我
    • 在您的原始帖子中,您使用data={voters} 将数组voters 传递给组件道具。但是当您调用setState 时,您更改的是state 对象,而不是voters 对象。您可以使用 data={state.voters} 告诉 React 观察 state 对象的变化,或者更好地使用单独的 useState 分配 voters,就像我的示例一样。
    • 似乎参考与价值的概念在这里让您感到困惑。网上有几十种材料解释了这个概念,快速搜索给了我this post,这似乎很有帮助。
    • 我用过data={state.voters},结果还是一样(表格不显示)
    • 嗯,有空我会在本地再次检查这个方法。您是否尝试过另一个修复 const [voters, setVoters] = useState([])
    【解决方案2】:

    欢迎使用 React 函数式编程。

    我没有你所有的依赖项,所以我试图用眼睛而不是实际编译来回答你的问题。

    更新状态时的一粒盐,而不是这样做:

    setState({
       ...state,
       ...yourstuffhere
    })
    

    这样做:

    setState(prevState => {
        return {
            ...prevState,
            ...yourstuffhere
        }
    });
    

    通过执行上述操作,您可以确保您已经更新了之前状态的数据。请记住,setState 是一个异步函数。除非您这样做,否则您可能会意外丢失一些数据。

    useEffect(() => {
        async function fetchData() {
            setState(prevState => { 
                return {
                    ...prevState, isLoading: true 
                }
            })
    
            let voters = await api.getAllVoters()
            setState(prevState => {
                return {
                    voters: voters.data.data, 
                    ...prevState,
                    isLoading: true,
                }
            })
        }
        fetchData()
        console.log(fetc)
    }, [])
    

    【讨论】:

    • 我试过了。但是我得到了这个错误:(匿名函数)70 | let voters = api.getAllVoters() 71 | setState((prevState) =&gt; { 72 | return { &gt; 73 | voters: voters.data.data, | ^ 74 | ...prevState, 75 | isLoading: true 76 | }
    猜你喜欢
    • 2021-11-24
    • 2022-01-17
    • 2020-01-02
    • 2017-09-27
    • 2020-02-01
    • 2021-12-10
    • 1970-01-01
    相关资源
    最近更新 更多