从0学习React(1)

发布于:2024-10-11 ⋅ 阅读:(117) ⋅ 点赞:(0)

上次在撰写关于 index.tsx 文件解析的文章时,我突然意识到文章篇幅过长,导致我忽略了很多代码的细节,只描述了 index.tsx 文件的大致结构。因此,在接下来的几篇文章中,我计划将 index.tsx 文件拆分成多个部分进行详细解析。我会尽力确保每个部分都讲得通透易懂,同时力求生动形象地呈现代码的每一个细节。

type PropsType = {
  loadMore: 'more' | 'noMore' | 'loading' | undefined;   //loadMore 的值可以控制一些组件的状态
  [key: string]: any;
};
/*
'more':通常在分页加载数据时,表示当前页面的数据已经加载完成,可以继续加载下一页的数据
'noMore':通常在分页加载数据时,表示所有数据已经加载完毕,没有更多数据可以加载了
'loading':表示正在加载数据。通常在发起数据请求时使用,表示当前正在加载数据,可能会显示一个加载动画或进度条
undefined:表示 loadMore 属性未定义或未初始化。这种情况下,loadMore 属性没有特定的状态,可能用于初始化或重置状态
*/

上面的代码我们称之为“类型定义”。实际上,这是 TypeScript 的语法。我在网上查了一下,发现这个代码其实可以省略,但如果你加上这些类型定义,代码会更加严谨和规范。

state: PropsType = {
    actionSheetIsOpen: false,
    searchType: '0,5,6,9',   //故障中心首页存储的故障单种类
    execStatus: null,
    faultStatusMap: new Map(),
    faultStatusList: [],
    loadMore: 'more',      //控制加载更多的状态
    page: {                //分页信息
      pageSize: 10,
      current: 1,
    },
    // tabList: [{ title: '公共故障' }, { title: '我的上报' }],
    listData: [],         //现在先定义为空数组,待会儿从后端拿到故障单后,每张故障单都会放到这里
    userInfo: getStorageSync(TOKEN_KEY),
    deviceCode: '',
    deviceId: '',
    reporterId: '',
    realExectorId: '',
    loading: false,       //控制加载状态
    selectedButton: null, // 用于跟踪选中的按钮
  };

这个代码是 React 中组件状态的初始化,目的是为组件提供默认值,确保组件在第一次渲染时有一个合理的初始状态。

因为每个组件都有自己的状态,你不可能让一个组件没有任何状态。那么,当组件需要状态时,状态的初始值是什么呢?组件在第一次渲染时必须要有状态值,否则它无法正确渲染。因此,这段代码的作用就是初始化状态,确保组件能够顺利渲染。

componentDidMount() {
    this.getBaseDefineOption();  //获取初始数据

    /*
    eventCenter.on 方法用于监听路由的 onShow 事件。
    当页面显示时,回调函数会被调用,回调函数中会通过 this.$instance?.router?.params 获取当前路由的参数,并根据参数执行相应的逻辑。
    */
    eventCenter.on(this.$instance.router!.onShow, () => {
      const params: any = this.$instance?.router?.params;  
console.log(JSON.stringify(params)+"params")  //params没有,证明监听不到路由参数

      if (params) {
        if (params.reporterId) {
          Taro.setNavigationBarTitle({
            title: '我的故障',
          });
        } else if (params.realExectorId) {
          Taro.setNavigationBarTitle({
            title: '我的维修',
          });
        }
        this.setState(
          {
            deviceId: params.deviceId,
            deviceCode: params.deviceCode,
            reporterId: params.reporterId,
            realExectorId: params.realExectorId,
          },
          () => {
            this.getList();
          }
        );
      } else {
        this.getList();
      }
    });
  }

这是生命周期函数。

既然提到了生命周期函数,我们来谈谈组件首次加载时的执行顺序:首先初始化状态(state),接着执行 render() 方法,最后执行 componentDidMount() 方法。一开始我不太明白什么是首次加载组件,于是我询问了一下 AI,了解到大致意思是这样的:比如我有一个故障中心,首次加载组件指的是用户第一次访问故障中心页面时,组件从创建到呈现的整个过程。

其实,故障中心可以被理解成一个大组件,或者称为容器组件。大组件通常会在 componentDidMount() 方法中进行数据获取,获取的数据会存储在大组件的状态(state)中,并通过将状态和回调函数作为属性传递给子组件。

对于大组件中的子组件,比如按钮,这些就是子组件。如果你点击一个按钮,执行的顺序应该是这样的:首先执行事件处理程序,然后执行 setState 更新组件状态,最后执行 render 重新渲染。

由此可见,点击一个按钮和进入故障中心背后的工作是不一样的。点击按钮时不会触发状态初始化和 componentDidMount,只会触发状态更新和重新渲染。

 onReachBottom() {
    if (this.state.loadMore == 'noMore') {
      return;
    }
    this.setState(
      {
        loadMore: 'loading',
        page: {
          ...this.state.page,
          current: this.state.page.current + 1,
        },
      },
      () => {

        const { 
          execStatus,
         } = this.state;

        if (execStatus !== undefined && execStatus !== null) {
          this.getList({ execStatus }, true);
        } else {
          this.getList({}, false); // 不传递 execStatus 参数
        }
      }
    );
  }

onReachBottom() 方法会在用户滚动到底部时触发,检查 loadMore 状态并决定是否加载更多数据。getList 方法根据 execStatus 的值加载不同状态的故障单,并更新组件的状态。

具体来说:

  1. 检查 loadMore 状态

    • 如果 loadMore 状态为 noMore,表示没有更多数据可以加载,函数直接返回,不执行任何操作。
  2. 更新 loadMore 状态和 page.current

    • 将 loadMore 状态设置为 loading,表示正在加载数据。
    • 增加 page.current 的值,即当前页数加一。
  3. 调用 getList 方法加载数据

    • 根据 execStatus 的值来决定加载哪些故障单:
      • 如果 execStatus 的值是 1,则继续加载未完成的故障单。
      • 如果 execStatus 的值是 2,则继续加载已完成的故障单。
getList = (/*type,loadMore*/ /*type = {}, loadMore = false*/ type: { execStatus?: number | null } = {}, loadMore: boolean = false) => {
    this.handleLoading(true);
    const {
      searchType,
      deviceCode,
      deviceId,
      reporterId,
      realExectorId,
      execStatus,
    } = this.state;
                                  // console.log(searchType+"测试");0569
                                  // console.log(deviceCode+"测试");null
                                  // console.log(deviceId+"测试");null
                                  // console.log(reporterId+"测试");null
                                  console.log(execStatus+"测试")

    let params: any = {
      ...this.state.page,
      ...type,
      
    };
    
    //console.log(JSON.stringify(params) + " 测试数据1");      {"pageSize":10,"current":1,"execStatus":2}         {"pageSize":10,"current":1}

    if (type && type.execStatus) {
      params.execStatus = type.execStatus;
    } else {
      params.statusSet = searchType;
    }

    //console.log(JSON.stringify(params) + " 测试数据2");        {"pageSize":10,"current":1,"execStatus":2}         {"pageSize":10,"current":1,"statusSet":"0,5,6,9"}

    if (deviceId) {
      params.deviceId = deviceId;
    }
    if (deviceCode) {
      params.deviceCode = deviceCode;
    }
    if (reporterId) {
      params.reporterId = reporterId;
    }
    if (realExectorId) {
      params.realExectorId = realExectorId;
    }
    

    //console.log(JSON.stringify(params) + " 测试数据3");        {"pageSize":10,"current":1,"execStatus":2}           {"pageSize":10,"current":1,"statusSet":"0,5,6,9"}



    getFaultCenterList(params)
      .then((result) => {
        if (result?.code === SUCCESS) {
          if (loadMore) {
            this.setState({
              listData: [...this.state.listData, ...result.data.list],             
              loadMore: result.data.list.length > 0 ? 'more' : 'noMore',
            });
          } 
          else {
            this.setState({
              listData: result.data.list,
              loadMore: result.data.total <= 10 ? 'noMore' : 'more',
              actionSheetIsOpen: false,
              page: { ...this.state.page, current: 1 },
            });
          }
        }
        this.handleLoading(false);
      })
      .catch(() => {
        this.setState({
          listData: [],
          loadMore: 'noMore',
        });
        this.handleLoading(false);
      });
  };

这段代码用于获取故障单列表,并且是整个程序中最关键的部分。

在“已完成”和“未完成”按钮的事件处理函数中,更新组件状态后会调用这个方法来获取故障单。接下来,我将以“已完成”按钮为例,详细介绍这段代码的工作原理。

const {
      searchType,
      deviceCode,
      deviceId,
      reporterId,
      realExectorId,
      execStatus,
    } = this.state;

这段代码使用了解构赋值。例如,当“已完成”按钮调用 getList() 方法时,解构赋值会获取“已完成”按钮的状态值。具体来说,这里会将 this.state.execStatus 的值(对于“已完成”按钮来说是 2)赋值给 getList() 方法的 execStatus 参数。

let params: any = {
      ...this.state.page,
      ...type,
      
    };

这个是JavaScript的语法,代码意思是:创建了一个名为 params 的对象,它通过对象展开运算符 (...) 将 this.state.page 和 type 对象的所有属性合并到 params 中。

if (type && type.execStatus) {
params.execStatus = type.execStatus;
} else {
params.statusSet = searchType;
}

这段代码检查 type 对象是否存在且 type.execStatus 是否有值。如果 type.execStatus 存在,则将其赋值给 params.execStatus;否则,将 searchType 的值赋给 params.statusSet

getFaultCenterList(params)
      .then((result) => {
        if (result?.code === SUCCESS) {
          if (loadMore) {
            this.setState({
              listData: [...this.state.listData, ...result.data.list],             
              loadMore: result.data.list.length > 0 ? 'more' : 'noMore',
            });
          } 
          else {
            this.setState({
              listData: result.data.list,
              loadMore: result.data.total <= 10 ? 'noMore' : 'more',
              actionSheetIsOpen: false,
              page: { ...this.state.page, current: 1 },
            });
          }
        }
        this.handleLoading(false);
      })
      .catch(() => {
        this.setState({
          listData: [],
          loadMore: 'noMore',
        });
        this.handleLoading(false);
      });
  };

这段代码通过解构赋值将多个属性赋值给 params 参数。params 作为请求参数被传递给后端接口,从而获取数据。获取到的数据会存储在 result 中。

如果 loadMore 为 true,表示需要加载更多数据,此时会将新获取的数据追加到现有的 listData 中;如果 loadMore 为 false,表示不需要加载更多数据,则会将 listData 重置为新获取的数据。

具体来说,从后端获取到 list[] 数据后,会将 list[] 数据放入 listData 中,listData 实际上是一个状态。其他状态的更新则根据具体情况而定,返回 true 或 false 时,更新的状态也会有所不同。

cancel2 = async () => {
    this.setState(
      { 
        execStatus: 2, 
        page: {
           ...this.state.page,
            current: 1,
           }
     },
      () => {
      this.getList({ execStatus: 2 }, false);
    });
  };

这段代码的功能是在点击“已完成”按钮时更新状态,并重新获取故障单的列表数据。

cancel1 = async () => {
    this.setState(
      { 
        execStatus: 1, 
        page: {
         ...this.state.page, 
         current: 1,
         } 
         
    }, 
    () => {
      this.getList({ execStatus: 1 }, false);
    });
  };

这段代码的功能是在点击“未完成”按钮时更新状态,并重新获取故障单的列表数据。

render() {
    const { faultStatusMap, faultStatusList, userInfo ,selectedButton}: any = this.state;
    const TaskStatuText = function(props) {
      const {
        status,
        realExectorId,
        realExectorName,
        pendingAcceptorId,
        pendingAcceptor,
      } = props.info;
    };
    const FaultCenterItem = () => {
      return (
        <View>
          {this.state.listData.map((item) => (
            <View
              className='container-item'
              key={item.id}
              onClick={() => this.toTaskList(item)}
            >
              <View className='title'>
                {item.deviceName} ( {item.devicePosition} )
              </View>
              <View className='des'>
                <View>
                  上报人/<Text className='name'>{item.reporterName}</Text>
                </View>
                <View>
                  <TaskStatuText info={item} />
                </View>
              </View>
              <View className='footer'>
                <View className='time'>{item.reportTime}</View>
                <View className='btn'>
                  <Text className='check'>查看详情</Text>
                  <View className='at-icon at-icon-chevron-right'></View>
                </View>
              </View>
            </View>
          ))}
        </View>
      );
    };

    return (
      
          <View className='two-buttons-container'>
          <AtButton
            className={`button ${selectedButton === 'cancel1' ? 'selected' : ''}`}
            onClick={this.cancel1}
          >
            未完成
          </AtButton>
          <AtButton
            className={`button ${selectedButton === 'cancel2' ? 'selected' : ''}`}
            onClick={this.cancel2}
          >
           已完成
          </AtButton>
        </View>


        <View className='container'>
          <FaultCenterItem />
        </View>
        
      </View>
    );
  }
}
export default Index;

未完待续......


网站公告

今日签到

点亮在社区的每一天
去签到