这篇博客记录实习的所见所闻1

1.react native中的navigation中的headerBackImage属性(设置返回按钮样式)

  • React Native Navigation中,headerBackImage是一个可选的属性,headerBackImage 属性最早出现在 @react-navigation/stack 5.0.0 版本中。在此之前,该属性可以通过使用 headerLeft 和自定义返回按钮组件来实现。,用于自定义路由堆栈导航栏中返回按钮的图像。默认情况下,返回按钮会显示操作系统的默认返回箭头图标。
  • 如果你想要使用自己的图标来代替默认返回箭头,可以在创建StackNavigator时为每个StackNavigator或者每个具有独立headerscreen设置 headerBackImage 属性。
  • 例如,你可以通过以下方式在整个stack navigator上设置该属性:
import { createStackNavigator } from '@react-navigation/stack';

const Stack = createStackNavigator();

function App() {
return (
<Stack.Navigator
screenOptions={{
headerBackImage: () => (
<Image
source={require('./path/to/your/image')}
style={{ width: 20, height: 20 }}
/>
),
}}
>
{/* Screens */}
</Stack.Navigator>
);
}
  • 或者,在单个screen上设置它,可以这样做:
import { useNavigation } from '@react-navigation/native';

function MyScreen() {
const navigation = useNavigation();

React.useLayoutEffect(() => {
navigation.setOptions({
headerBackImage: () => (
<Image
source={require('./path/to/your/image')}
style={{ width: 20, height: 20 }}
/>
),
});
}, [navigation]);

// ...

需要注意的是,headerBackImage只影响路由堆栈导航栏的返回按钮图标,并不影响手势和其他返回交互的行为。

2.static在类组件中的作用是什么?(react-native/react)

  • React 的类组件中,static 关键字用于定义静态属性或静态方法。这些属性或方法不会被实例化对象所继承,而是直接属于类本身。因此,它们可以在任何地方被访问,无需创建类的实例。

在类组件中,static 可以在以下场景下使用:

  1. 1.定义静态属性
    • 静态属性是指属于类本身而非实例对象的属性,可以通过类名直接访问。例如,你可以在一个 React 类组件中定义一个静态属性来存储组件的一些默认状态:**
      class MyComponent extends React.Component {
      static defaultProps = {
      count: 0,
      }

      // ...
      }
  2. 定义静态方法
    • 静态方法是指属于类本身而非实例对象的方法,可以通过类名直接调用。例如,你可以在一个React类组件中定义一个静态方法来实现一些通用的逻辑:
      class MyComponent extends React.Component {
      static doSomething() {
      // ...
      }

      // ...
      }

      需要注意的是,由于静态方法和静态属性是属于类本身而非实例对象的,所以你不能在这些静态成员中访问 this,并且无法访问实例对象的属性或方法。

3.navigationOptions结合static在类组件中的使用以及为什么一定要结合static来使用才能在类组件内部配置navigationOptions(React-native)

  • navigationOptionsReact Navigation 中用于配置页面导航选项的属性,它可以被设置为一个对象或一个返回对象的函数。在函数组件中,我们可以使用 useNavigationOptions 钩子来动态配置导航选项。而在类组件中,由于缺乏钩子这种函数式编程的特性,我们需要使用static关键字来定义静态属性。
  • 具体地说,当你在类组件中定义一个navigationOptions对象时,这个对象是作为该类的实例属性存在的。而当你在一个屏幕中使用这个类时,React Navigation 会利用这个对象来设置导航栏等选项。但是,如果你不使用static关键字来定义这个对象,这个对象会成为该类所有实例的共享属性,这可能会导致一些意外的问题。
  • 通过使用static关键字,你可以确保 navigationOptions 对象只属于该类本身而非该类的实例对象,并且不受到其他实例对象的影响。这样,在不同的屏幕中使用相同的类时,每个屏幕都可以有自己独立的导航选项。
  • React Navigation 组件中,可以使用 navigationOptions 属性来配置屏幕的导航选项。而在类组件中,可以结合 static 关键字使用 navigationOptions 来定义静态属性。

例如,在一个使用 createStackNavigator 创建的堆栈导航中,你可以在类组件中定义 navigationOptions 静态属性来配置页面的标题:

class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Home',
};

// ...
}

在上面的例子中,navigationOptions 是一个静态属性,它的值是一个对象,包含了要设置的选项,比如 title。这个静态属性会被堆栈导航器用于生成页面的导航栏,显示为一个标题为 "Home" 的页面。
需要注意的是,在类组件中定义 navigationOptions 静态属性时,它只能访问到类本身的静态成员,不能访问实例对象的属性或方法。如果你需要访问实例对象的属性或方法,可以在渲染方法中使用this.propsthis.state 来获取。

4.使用函数式组件中的useEffect钩子模拟类组件中的所有生命周期

  • 当使用函数式组件时,可以使用 useEffect 钩子来模拟类组件中的各个生命周期方法。下面是一些常见的情况以及如何用useEffect来模拟它们:

1.模拟 componentDidMount

  • 在类组件中,componentDidMount 是在组件挂载后调用的生命周期方法,可以在其中做一些初始化操作。在函数式组件中,可以使用 useEffect 来实现相同的功能:
useEffect(() => {
// 这里执行初始化操作
}, []);

2.模拟 componentDidUpdate

  • 在类组件中,componentDidUpdate 是在组件更新后调用的生命周期方法,可以在其中进行一些副作用操作。在函数式组件中,也可以使用 useEffect 来模拟 componentDidUpdate
···js
useEffect(() => {
// 这里执行副作用操作
}, [props.someProp]);

3.模拟 componentWillUnmount

  • 在类组件中,componentWillUnmount 是在组件卸载前调用的生命周期方法,可以在其中进行一些清理操作。在函数式组件中,也可以使用 useEffect 来模拟 componentWillUnmount:
useEffect(() => {
return () => {
// 这里执行清理操作
};
}, []);

在这个例子中,我们在useEffect中返回一个函数,这个函数会在组件卸载时被调用。这与 componentWillUnmount 的行为相似。需要注意的是,useEffect 和类组件中的生命周期不完全等价,因为它们有一些细微的差别。例如,在类组件中,可以取消更新或者阻止组件重新渲染,但在函数式组件中则不能这样做。因此,在实现功能相同时,应该根据具体情况选择合适的方式。

5.usememo结合useEffect来实现子组件更新但父组件不更新的操作

  • React 中,可以使用 useMemo 钩子来缓存计算结果,并且只有在依赖项发生变化时才重新计算。结合 useEffect,我们可以实现子组件更新但父组件不更新的操作。
  • 具体地说,我们可以在父组件中使用 useMemo 缓存需要传递给子组件的数据,并将这个数据作为依赖项传递给 useEffect。这样,在子组件更新时,useMemo 的缓存数据不会失效,只有在父组件更新时才会重新计算。
import { useState, useMemo, useEffect } from 'react';
import ChildComponent from './ChildComponent';

function ParentComponent(props) {
const [count, setCount] = useState(0);

// 使用 useMemo 缓存需要传递给子组件的数据
const memoizedData = useMemo(() => {
return {
count,
someProp: props.someProp,
};
}, [ props.someProp]);

// 使用 useEffect 监听 memoizedData 的变化
useEffect(() => {
console.log('memoizedData has changed:', memoizedData);
}, [memoizedData]);

return (
<div>
<button onClick={() => setCount(count + 1)}>
Click me ({count})
</button>
<ChildComponent data={memoizedData} />
</div>
);
}

在这个例子中,我们定义了一个 ParentComponent 父组件和一个 ChildComponent 子组件。在 ParentComponent 中,我们使用 useState 定义了一个状态变量 count,并在按钮的点击事件中更新这个变量。我们还使用 useMemo 缓存了需要传递给子组件的数据,并将这个数据作为依赖项传递给了 useEffect。在 ChildComponent 中,我们只是简单地输出父组件传递过来的数据。
当你运行这个代码时,你会发现每次在父组件中点击按钮更新 count 变量时,控制台打印的消息仅仅是子组件更新了,而父组件没有更新。这是因为我们使用了 useMemo 来缓存需要传递给子组件的数据,并且只有在count props.someProp 变化时才会重新计算缓存数据。
需要注意的是,在实际开发中,如果你想要避免不必要的渲染,应该尽可能地将 useMemouseCallback 应用到所有合适的地方,并使用合适的依赖项来控制它们的触发时机。

6.讲讲useCallback和useMemo的区别

  • useCallback useMemo 都是 React 中的钩子函数,用于优化性能和避免不必要的重新渲染。虽然它们有一些相似之处,但它们的作用和使用方法有所不同。
  • useCallback 的作用是在函数组件的渲染周期内缓存回调函数,并且只有在某个依赖项改变时才重新创建这个回调函数。这样,在父组件需要向子组件传递回调函数时,可以使用 useCallback 来确保子组件不会因为父组件的重新渲染而不必要地更新。
import { useState, useCallback } from 'react';

function ParentComponent() {
const [count, setCount] = useState(0);

const handleClick = useCallback(() => {
console.log('Button clicked!');
}, []);

return (
<div>
<button onClick={() => setCount(count + 1)}>
Click me ({count})
</button>
<ChildComponent onClick={handleClick} />
</div>
);
}

function ChildComponent(props) {
return <button onClick={props.onClick}>Click me</button>;
}

在这个例子中,我们定义了一个 ParentComponent 父组件和一个 ChildComponent 子组件。在 ParentComponent 中,我们使用 useState 定义了一个状态变量 count,并在按钮的点击事件中更新这个变量。我们还使用 useCallback 缓存了一个回调函数 handleClick,并将它传递给了子组件 ChildComponent。在ChildComponent中,我们只是简单地输出父组件传递过来的回调函数。

  • useMemo 的作用是在函数组件的渲染周期内缓存计算结果,并且只有在某个依赖项改变时才重新计算这个结果。这样,在某些昂贵的计算场景下,可以使用 useMemo 来避免重复计算。下面是一个示例代码:
import { useMemo } from 'react';

function MyComponent(props) {
const memoizedValue = useMemo(() => {
// 这里可以进行一些昂贵的计算
return props.someProp * 2;
}, [props.someProp]);

return <div>The value is {memoizedValue}.</div>;
}

在这个例子中,我们定义了一个 MyComponent 函数组件,并使用 useMemo 缓存了一个计算结果 memoizedValue。在 useMemo 的回调函数中,我们进行了一些昂贵的计算,并将结果返回给 useMemo。我们还将 props.someProp 作为依赖项传递给了 useMemo,以确保只有在这个属性变化时才会重新计算缓存数据。
总的来说,useCallbackuseMemo 都是用于优化性能和避免不必要的重新渲染的工具。它们的主要区别在于应用场景和使用方法。如果你需要缓存一个回调函数并将它传递给子组件,那么可以使用 useCallback;如果你需要缓存一些计算结果并在渲染周期内重复使用,那么可以使用 useMemo

7. 在react 中的函数式组件hooksuseState只能一次处理一个状态吗?那如果在这个组件中我要维护多个状态呢?每一次更新多个状态都要调用一次对应的更新方法,这样不是造成了多次渲染组件吗?如何解决这个问题?

  • React 中的函数式组件中,useState 钩子只能处理一个状态。如果要维护多个状态,则需要使用多个useState钩子。如果在组件中需要维护的状态较多,而且频繁地更新这些状态可能会导致性能问题,可以考虑使用 useReducer 钩子来替代 useState 钩子。useReducer 可以将多个状态合并为一个状态对象,并提供了一种更加灵活的状态管理方式。例如:
import { useReducer } from 'react';

function reducer(state, action) {
switch (action.type) {
case 'UPDATE_NAME':
return { ...state, name: action.payload };
case 'UPDATE_AGE':
return { ...state, age: action.payload };
default:
throw new Error();
}
}

function MyComponent() {
const [state, dispatch] = useReducer(reducer, {
name: 'Alice',
age: 20,
});

// 在事件处理函数中更新状态
const handleClick = () => {
dispatch({ type: 'UPDATE_NAME', payload: 'Bob' });
dispatch({ type: 'UPDATE_AGE', payload: 30 });
};

return (
<div>
<p>Name: {state.name}</p>
<p>Age: {state.age}</p>
<button onClick={handleClick}>Update</button>
</div>
);
}

在这个例子中,我们使用了 useReducer 钩子并定义了一个 reducer 函数来处理多个状态的更新操作。需要注意的是,在使用 useReducer 时,每次调用 dispatch 函数都会触发一次组件重新渲染,因此也应该避免频繁地更新状态。