实例讲解:React Hook的思考
翻译原文:https://wattenberger.com/blog/react-hooks React在一年前引入了hooks,对于许多开发人员来说,它们已经改变了我们的编码习惯,但我想谈一谈从React类组件切换到功能组件基本变化。 使用类组件,我们将更新放置在特定的生命周期事件中 ❝ 类组成中 ❞ classChartextendsComponent{componentDidMount(){//挂载时}componentDidUpdate(prevProps){if(prevProps.data==props.data)return//数据更新时}componentWillUnmount(){//卸载时}render(){return(<svgclassName="Chart"/>)}} 在功能组件中,我们改为使用useEffect挂钩在主要生命周期事件期间运行代码。 ❝ 功能组件中 ❞ constChart=({data})=>{useEffect(()=>{//挂载时//数据更新时return()=>{}},[data])return(<svgclassName="Chart"/>)} 此时,您可能在想:所以,useEffect() 这只是一种进入生命周期事件的新方法!但是这种理解是错误的! 嗯,让我们看一个具体的例子,看看有什么区别。例如,如果我们有一个计算量大的 getDataWithinRange()函数,该函数基于指定的函数返回经过过滤的数据集,该dateRange怎么办?因为getDataWithinRange()运行需要一些时间,所以我们希望将其存储在组件的state对象中,并且仅在dateRange发生更改时进行更新。 类组成: 对于生命周期事件,我们需要一站式处理所有更改。我们的想法如下所示: 当我们的组件加载时,以及props更改时(其实就是dateRange),就相对的更新data 大致如下: classChartextendsComponent{state={data:null,}componentDidMount(){constnewData=getDataWithinRange(this.props.dateRange)this.setState({data:newData})}componentDidUpdate(prevProps){if(prevProps.dateRange!=this.props.dateRange){constnewData=getDataWithinRange(this.props.dateRange)this.setState({data:newData})}}render(){return(<svgclassName="Chart"/>)}} 功能组成 在功能组件中,我们需要考虑哪些值保持同步。每次更新的流程更像以下语句: 保持dateRange同步data 大致如下: constChart=({dateRange})=>{const[data,setData]=useState()useEffect(()=>{//假设getDataWithinRange在外部定义了,暂时不考虑这个constnewData=getDataWithinRange(dateRange)setData(newData)},[dateRange])return(<svgclassName="Chart"/>)} 看到将变量保持同步的方式简单多了是吗?不仅仅是代码量减少了! 类组成 classChartextendsComponent{state={data:null,}componentDidMount(){constnewData=getDataWithinRange(this.props.dateRange)this.setState({data:newData})}componentDidUpdate(prevProps){if(prevProps.dateRange!=this.props.dateRange){constnewData=getDataWithinRange(this.props.dateRange)this.setState({data:newData})}}render(){return(<svgclassName="Chart"/>)}} 功能组成 实际上,最后一个示例仍在类组件中。我们存储data是state为了防止每次组件更新时重新计算它。 但是我们不再需要使用state!而是使用useMemo(),仅data当其依赖项数组更改时才会重新计算。 constChart=({dateRange})=>{constdata=useMemo(()=>(getDataWithinRange(dateRange)),[dateRange])return(<svgclassName="Chart"/>)} ❝ 注意,我们可以只在函数内部定义变量。 ❞ constChart=({dateRange})=>{constnewData=getDataWithinRange(dateRange)return(<svgclassName="Chart"/>)} 除非我们的getDataWithinRange()函数在计算上消耗很大,否则这就是非常完美。我平时使用useMemo用的非常多,特别是在处理大型数据集时,以保持性能。 考虑使用context 想象一下,我们有许多需要计算的值,但是它们取决于不同的props。例如,我们需要计算: 我们 data在我们的 dateRange变化 的 dimensions图表的时 margins变化 我们 scales在我们的 data变化 类组成 classChartextendsComponent{state={data:null,dimensions:null,xScale:null,yScale:null,}componentDidMount(){constnewData=getDataWithinRange(this.props.dateRange)this.setState({data:newData})this.setState({dimensions:getDimensions()})this.setState({xScale:getXScale()})this.setState({yScale:getYScale()})}componentDidUpdate(prevProps,prevState){if(prevProps.dateRange!=this.props.dateRange){constnewData=getDataWithinRange(this.props.dateRange)this.setState({data:newData})}if(prevProps.margins!=this.props.margins){this.setState({dimensions:getDimensions()})}if(prevState.data!=this.state.data){this.setState({xScale:getXScale()})this.setState({yScale:getYScale()})}}render(){return(<svgclassName="Chart"/>)}} 功能组成 在我们的功能组件中,我们只需要简单的几句: constChart=({dateRange,margins})=>{constdata=useMemo(()=>(getDataWithinRange(dateRange)),[dateRange])constdimensions=useMemo(getDimensions,[margins])constxScale=useMemo(getXScale,[data])constyScale=useMemo(getYScale,[data])return(<svgclassName="Chart"/>)} 在我们的示例中,我们的类组件为什么会笨拙? 这是因为我们有很多的声明代码来去同步变量props和state,但在功能组件,我们只注重什么保持同步。 平时尽可能使用useMemo()钩子-让我们来实现功能效果。 现在再来一个需求 好的!继续前进-如果我们scales需要根据dimensions图表的图表进行更改怎么办? 类组成 在我们的类组件中,我们需要比较我们prevState和current state classChartextendsComponent{state={data:null,dimensions:null,xScale:null,yScale:null,}componentDidMount(){constnewData=getDataWithinRange(this.props.dateRange)this.setState({data:newData})this.setState({dimensions:getDimensions()})this.setState({xScale:getXScale()})this.setState({yScale:getYScale()})}componentDidUpdate(prevProps,prevState){if(prevProps.dateRange!=this.props.dateRange){constnewData=getDataWithinRange(this.props.dateRange)this.setState({data:newData})}if(prevProps.margins!=this.props.margins){this.setState({dimensions:getDimensions()})}if(prevState.data!=this.state.data||prevState.dimensions!=this.state.dimensions){this.setState({xScale:getXScale()})this.setState({yScale:getYScale()})}}render(){return(<svgclassName="Chart"/>)}} 功能组成 constChart=({dateRange,margins})=>{constdata=useMemo(()=>(getDataWithinRange(dateRange)),[dateRange])constdimensions=useMemo(getDimensions,[margins])constxScale=useMemo(getXScale,[data,dimensions])constyScale=useMemo(getYScale,[data,dimensions])return(<svgclassName="Chart"/>)} 是不是爽歪歪! 保持代码简洁 较短的代码不一定更易于阅读,但是使我们的组件尽可能地整洁无疑是一个优势! 最后 关注我们添加【前端进阶技术群】,回复:【资料包】领取前端进阶资料包 在看、转发支持作者❤️ 本文分享自微信公众号 - 前端人(FrontendPeople)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。