浏览代码

context 封装

MinJieLiu 7 年之前
父节点
当前提交
dd7626124a
共有 6 个文件被更改,包括 126 次插入86 次删除
  1. 26 23
      examples/simple.tsx
  2. 20 11
      src/PhotoConsumer.tsx
  3. 24 14
      src/PhotoProvider.tsx
  4. 45 35
      src/PhotoSlider.tsx
  5. 2 0
      src/index.tsx
  6. 9 3
      src/photo-context.ts

+ 26 - 23
examples/simple.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import ReactDOM from 'react-dom';
 import styled from 'styled-components';
-import { PhotoSlider } from '../src';
+import { PhotoProvider, PhotoConsumer } from '../src';
 
 const Container = styled.div`
   font-size: 32px;
@@ -13,38 +13,41 @@ const Header = styled.header`
   border-bottom: 1px solid #ccc;
 `;
 
+const ImageList = styled.div`
+  padding: 40px;
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+`;
+
+const SmallImage = styled.img`
+  margin-right: 20px;
+  margin-bottom: 20px;
+  width: 100px;
+  height: 100px;
+  cursor: pointer;
+`;
+
 class Example extends React.Component {
   state = {
     photoImages: ['1.png', '2.jpg', '1.png'],
-    photoVisible: true,
-    photoIndex: 1,
   };
 
-  handlePhotoClose = () => {
-    this.setState({
-      photoVisible: false,
-    });
-  }
-
-  handleVisibleChange = (photoIndex) => {
-    this.setState({
-      photoIndex,
-    });
-  }
-
   render() {
-    const { photoImages, photoVisible, photoIndex } = this.state;
+    const { photoImages } = this.state;
 
     return (
       <Container>
         <Header>React 图片预览组件</Header>
-        <PhotoSlider
-          images={photoImages}
-          index={photoIndex}
-          onIndexChange={this.handleVisibleChange}
-          visible={photoVisible}
-          onClose={this.handlePhotoClose}
-        />
+        <PhotoProvider>
+          <ImageList>
+            {photoImages.map((item, index) => (
+              <PhotoConsumer key={index} src={item}>
+                <SmallImage src={item} />
+              </PhotoConsumer>
+            ))}
+          </ImageList>
+        </PhotoProvider>
       </Container>
     );
   }

+ 20 - 11
src/PhotoConsumer.tsx

@@ -1,17 +1,20 @@
 import React from 'react';
 import uniqueId from 'lodash.uniqueid';
-import PhotoContext from './photo-context';
+import PhotoContext, {
+  onShowType,
+  addItemType,
+  removeItemType,
+} from './photo-context';
 
 export interface IPhotoViewItem {
   src: string;
-  children: React.ReactElement<any>;
-  onShow: (dataKey) => void;
-  addItem: (dataKey: string, src: string) => void;
-  removeItem: (dataKey) => void;
+  children?: React.ReactElement<any>;
+  onShow: onShowType;
+  addItem: addItemType;
+  removeItem: removeItemType;
 }
 
 class PhotoViewItem extends React.Component<IPhotoViewItem> {
-
   private dataKey: string = uniqueId();
 
   componentDidMount() {
@@ -28,9 +31,11 @@ class PhotoViewItem extends React.Component<IPhotoViewItem> {
     const { onShow, children } = this.props;
     onShow(this.dataKey);
 
-    const { onClick } = children.props;
-    if (onClick) {
-      onClick(e);
+    if (children) {
+      const { onClick } = children.props;
+      if (onClick) {
+        onClick(e);
+      }
     }
   }
 
@@ -47,10 +52,14 @@ class PhotoViewItem extends React.Component<IPhotoViewItem> {
 
 export interface IPhotoConsumer {
   src: string;
-  children: React.ReactElement<any>;
+  children?: React.ReactElement<any>;
 }
 
-const PhotoConsumer: React.SFC<IPhotoConsumer> = ({ src, children, ...restProps }) => (
+const PhotoConsumer: React.SFC<IPhotoConsumer> = ({
+  src,
+  children,
+  ...restProps
+}) => (
   <PhotoContext.Consumer>
     {value => (
       <PhotoViewItem

+ 24 - 14
src/PhotoProvider.tsx

@@ -1,5 +1,9 @@
 import React from 'react';
-import PhotoContext from './photo-context';
+import PhotoContext, {
+  onShowType,
+  addItemType,
+  removeItemType,
+} from './photo-context';
 import PhotoSlider from './PhotoSlider';
 import { dataType } from './types';
 
@@ -19,10 +23,9 @@ type PhotoProviderState = {
   data: dataType[];
   visible: boolean;
   index: number;
-  onShow: (dataKey) => void;
-  onClose: () => void;
-  addItem: (dataKey: string, src: string) => void;
-  removeItem: (dataKey) => void;
+  onShow: onShowType;
+  addItem: addItemType;
+  removeItem: removeItemType;
 };
 
 export default class PhotoProvider extends React.Component<
@@ -39,23 +42,30 @@ export default class PhotoProvider extends React.Component<
       addItem: this.handleAddItem,
       removeItem: this.handleRemoveItem,
       onShow: this.handleShow,
-      onClose: this.handleClose,
     };
   }
 
-  handleAddItem = item => {
+  handleAddItem = (dataKey: string, src: string) => {
     this.setState(prev => ({
-      data: prev.data.concat(item),
+      data: prev.data.concat({
+        dataKey,
+        src,
+      }),
     }));
   }
 
-  handleRemoveItem = dataKey => {
-    this.setState(prev => ({
-      data: prev.data.filter(item => item.dataKey !== dataKey),
-    }));
+  handleRemoveItem = (dataKey: string) => {
+    this.setState(({ data, index }) => {
+      const nextData = data.filter(item => item.dataKey !== dataKey);
+      const nextEndIndex = nextData.length - 1;
+      return {
+        data: nextData,
+        index: Math.min(nextEndIndex, index),
+      };
+    });
   }
 
-  handleShow = (dataKey) => {
+  handleShow = (dataKey: string) => {
     const { data } = this.state;
     this.setState({
       visible: true,
@@ -69,7 +79,7 @@ export default class PhotoProvider extends React.Component<
     });
   }
 
-  handleIndexChange = (index) => {
+  handleIndexChange = (index: number) => {
     this.setState({
       index,
     });

+ 45 - 35
src/PhotoSlider.tsx

@@ -7,7 +7,7 @@ import { maxMoveOffset, defaultOpacity } from './variables';
 
 export interface IPhotoSliderProps {
   // 图片列表
-  images: string[];
+  images: (string | dataType)[];
   // 图片当前索引
   index?: number;
   // 可见
@@ -35,7 +35,7 @@ type PhotoSliderState = {
   photoIndex: number;
 
   // 图片处于触摸的状态
-  touched: boolean,
+  touched: boolean;
   // Reach 开始时 x 坐标
   lastPageX: number | undefined;
   // Reach 开始时 y 坐标
@@ -53,7 +53,10 @@ export default class PhotoSlider extends React.Component<
   static displayName = 'PhotoSlider';
 
   static getDerivedStateFromProps(nextProps, prevState) {
-    if (nextProps.index !== undefined && nextProps.index !== prevState.photoIndex) {
+    if (
+      nextProps.index !== undefined &&
+      nextProps.index !== prevState.photoIndex
+    ) {
       return {
         photoIndex: nextProps.index,
         translateX: -window.innerWidth * nextProps.index,
@@ -111,13 +114,10 @@ export default class PhotoSlider extends React.Component<
         touched: true,
         lastPageY,
         backdropOpacity: Math.max(
-          Math.min(defaultOpacity, defaultOpacity - (offsetPageY / 100 / 2)),
+          Math.min(defaultOpacity, defaultOpacity - offsetPageY / 100 / 2),
           defaultOpacity / 6,
         ),
-        photoScale: Math.max(
-          Math.min(1, 1 - (offsetPageY / 100 / 10)),
-          0.6,
-        ),
+        photoScale: Math.max(Math.min(1, 1 - offsetPageY / 100 / 10), 0.6),
       };
     });
   }
@@ -157,7 +157,7 @@ export default class PhotoSlider extends React.Component<
     let currentTranslateX = -innerWidth * photoIndex;
     let currentPhotoIndex = photoIndex;
     // 下一张
-    if (offsetPageX < - maxMoveOffset && photoIndex < images.length - 1) {
+    if (offsetPageX < -maxMoveOffset && photoIndex < images.length - 1) {
       currentPhotoIndex = photoIndex + 1;
       currentTranslateX = -innerWidth * currentPhotoIndex;
       if (onIndexChange) {
@@ -209,32 +209,42 @@ export default class PhotoSlider extends React.Component<
             className={maskClassName}
             style={{ background: `rgba(0, 0, 0, ${backdropOpacity})` }}
           />
-          {images.map((src, index) => {
-            return (
-              <PhotoView
-                key={src + index}
-                src={src}
-                onReachTopMove={this.handleReachVerticalMove}
-                onReachBottomMove={this.handleReachVerticalMove}
-                onReachRightMove={index < images.length - 1
-                  ? this.handleReachHorizontalMove
-                  : undefined}
-                onReachLeftMove={index > 0 ? this.handleReachHorizontalMove : undefined}
-                onReachUp={this.handleReachUp}
-                photoScale={photoIndex === index ? photoScale : 1}
-                wrapClassName={viewClassName}
-                className={imageClassName}
-                style={{
-                  left: `${innerWidth * index}px`,
-                  WebkitTransform: transform,
-                  transform,
-                  transition: touched
-                    ? undefined
-                    : 'transform 0.6s cubic-bezier(0.25, 0.8, 0.25, 1)',
-                }}
-              />
-            );
-          })}
+          {images
+            .map((item: string | dataType, index) => {
+              const isStrItem = typeof item === 'string';
+              return (
+                <PhotoView
+                  key={
+                    isStrItem
+                      ? (item as string) + index
+                      : (item as dataType).dataKey
+                  }
+                  src={isStrItem ? (item as string) : (item as dataType).src}
+                  onReachTopMove={this.handleReachVerticalMove}
+                  onReachBottomMove={this.handleReachVerticalMove}
+                  onReachRightMove={
+                    index < images.length - 1
+                      ? this.handleReachHorizontalMove
+                      : undefined
+                  }
+                  onReachLeftMove={
+                    index > 0 ? this.handleReachHorizontalMove : undefined
+                  }
+                  onReachUp={this.handleReachUp}
+                  photoScale={photoIndex === index ? photoScale : 1}
+                  wrapClassName={viewClassName}
+                  className={imageClassName}
+                  style={{
+                    left: `${innerWidth * index}px`,
+                    WebkitTransform: transform,
+                    transform,
+                    transition: touched
+                      ? undefined
+                      : 'transform 0.6s cubic-bezier(0.25, 0.8, 0.25, 1)',
+                  }}
+                />
+              );
+            })}
           {overlay}
         </SlideWrap>
       );

+ 2 - 0
src/index.tsx

@@ -1,2 +1,4 @@
 export { default as PhotoView } from './PhotoView';
 export { default as PhotoSlider } from './PhotoSlider';
+export { default as PhotoProvider } from './PhotoProvider';
+export { default as PhotoConsumer } from './PhotoConsumer';

+ 9 - 3
src/photo-context.ts

@@ -1,7 +1,13 @@
 import React from 'react';
 
+export type onShowType = (dataKey?: string) => void;
+
+export type addItemType = (dataKey?: string, src?: string) => void;
+
+export type removeItemType = (dataKey?: string) => void;
+
 export default React.createContext({
-  onShow(dataKey) {},
-  addItem(dataKey, src) {},
-  removeItem(dataKey) {},
+  onShow() {},
+  addItem() {},
+  removeItem() {},
 });