Repository: freeCodeCamp/freeCodeCamp
File: client/src/components/profile/components/portfolio.tsx (TypeScript)

Code before:
    url: '',
    image: ''
  };
}

function createFindById(id: string) {
  return (p: PortfolioProjectData) => p.id === id;
}

class PortfolioSettings extends Component<PortfolioProps, PortfolioState> {
  validationImage: HTMLImageElement;
  static displayName: string;
  constructor(props: PortfolioProps) {
    super(props);
    this.validationImage = new Image();
    const { portfolio = [] } = props;

    this.state = {
      portfolio: [...portfolio],
      unsavedItemId: null,
      isImageValid: {
        state: 'success',
        message: ''
      }
    };
  }

  toggleEditing = () => {
    this.props.setIsEditing(false);
  };

  createOnChangeHandler =
    (id: string, key: 'description' | 'image' | 'title' | 'url') =>
    (e: React.ChangeEvent<HTMLInputElement>) => {
      e.preventDefault();
      const userInput = e.target.value.slice();
      this.setState(state => {
        const { portfolio: currentPortfolio } = state;
        const mutablePortfolio = currentPortfolio.slice(0);
        const index = findIndex(currentPortfolio, p => p.id === id);

        mutablePortfolio[index] = {
          ...mutablePortfolio[index],
          [key]: userInput
        };

        if (key === 'image' && userInput) {
          void this.validateImageLoad(userInput).then(imageValidation => {
            this.setState({ isImageValid: imageValidation });
          });
        } else if (key === 'image' && !userInput) {

Review comment (suggestion):
I recommend sharing the original `checkIfValidImage` function that's currently in `about.tsx`. The logic is common to the two approaches, so we might as well share the code. We already have client/src/components/profile/components/utils/utils.ts which seems like the obvious home for `checkIfValidImage`

Code after:
  return (p: PortfolioProjectData) => p.id === id;
}

const PortfolioSettings = (props: PortfolioProps) => {
  const {
    t,
    portfolio: initialPortfolio = [],
    setIsEditing,
    updatePortfolio
  } = props;
  const [portfolio, setPortfolio] = useState(initialPortfolio);
  const [unsavedItemId, setUnsavedItemId] = useState<string | null>(null);
  const [imageValidation, setImageValid] = useState<ProfileValidation>({
    state: 'success',
    message: ''
  });

  const checkIfValidImage = (url: string): Promise<ProfileValidation> => {
    const img = new Image();

    return new Promise(resolve => {
      img.onerror = () =>
        resolve({ state: 'error', message: t('validation.url-not-image') });
      img.onload = () => resolve({ state: 'success', message: '' });
      img.src = url;
    });
  };

  const createOnChangeHandler =
    (id: string, key: 'description' | 'image' | 'title' | 'url') =>
    (e: React.ChangeEvent<HTMLInputElement>) => {
      e.preventDefault();
      const userInput = e.target.value.slice();
      setPortfolio(prevPortfolio => {
        const mutablePortfolio = [...prevPortfolio];
        const index = findIndex(prevPortfolio, p => p.id === id);
        mutablePortfolio[index] = {
          ...mutablePortfolio[index],
          [key]: userInput
        };
        if (key === 'image' && userInput) {
          void checkIfValidImage(userInput).then(imageValidation => {
            setImageValid(imageValidation);
          });
        } else if (key === 'image' && !userInput) {
          setImageValid({ state: 'success', message: '' });
        }
        return mutablePortfolio;
      });
    };