Codegen SDK provides powerful APIs for modernizing React codebases. This guide will walk you through common React modernization patterns.Common use cases include:
Here’s how to convert React class components to functional components:
Copy
Ask AI
# Find all React class componentsfor class_def in codebase.classes: # Skip if not a React component if not class_def.is_jsx or "Component" not in [base.name for base in class_def.bases]: continue print(f"Converting {class_def.name} to functional component") # Extract state from constructor constructor = class_def.get_method("constructor") state_properties = [] if constructor: for statement in constructor.code_block.statements: if "this.state" in statement.source: # Extract state properties state_properties = [prop.strip() for prop in statement.source.split("{")[1].split("}")[0].split(",")] # Create useState hooks for each state property state_hooks = [] for prop in state_properties: hook_name = f"[{prop}, set{prop[0].upper()}{prop[1:]}]" state_hooks.append(f"const {hook_name} = useState(null);") # Convert lifecycle methods to effects effects = [] if class_def.get_method("componentDidMount"): effects.append(""" useEffect(() => { // TODO: Move componentDidMount logic here }, []); """) if class_def.get_method("componentDidUpdate"): effects.append(""" useEffect(() => { // TODO: Move componentDidUpdate logic here }); """) # Get the render method render_method = class_def.get_method("render") # Create the functional component func_component = f"""const {class_def.name} = ({class_def.get_method("render").parameters[0].name}) => {{ {chr(10).join(state_hooks)} {chr(10).join(effects)} {render_method.code_block.source}}}""" # Replace the class with the functional component class_def.edit(func_component) # Add required imports file = class_def.file if not any("useState" in imp.source for imp in file.imports): file.add_import("import { useState, useEffect } from 'react';")
# Find components using legacy patternsfor function in codebase.functions: if not function.is_jsx: continue # Look for common legacy patterns for call in function.function_calls: # Convert withRouter to useNavigate if call.name == "withRouter": # Add useNavigate import function.file.add_import( "import { useNavigate } from 'react-router-dom';" ) # Add navigate hook function.insert_before_first_return("const navigate = useNavigate();") # Replace history.push calls for history_call in function.function_calls: if "history.push" in history_call.source: history_call.edit( history_call.source.replace("history.push", "navigate") ) # Convert lifecycle methods in hooks elif call.name == "componentDidMount": call.parent.edit("""useEffect(() => { // Your componentDidMount logic here}, []);""")
Add proper prop types and TypeScript interfaces based on how props are used:
Copy
Ask AI
# Add TypeScript interfaces for propsfor function in codebase.functions: if not function.is_jsx: continue # Get props parameter props_param = function.parameters[0] if function.parameters else None if not props_param: continue # Collect used props used_props = set() for prop_access in function.function_calls: if f"{props_param.name}." in prop_access.source: prop_name = prop_access.source.split(".")[1] used_props.add(prop_name) # Create interface if used_props: interface_def = f"""interface {function.name}Props {{ {chr(10).join(f' {prop}: any;' for prop in used_props)}}}""" function.insert_before(interface_def) # Update function signature function.edit(function.source.replace( f"({props_param.name})", f"({props_param.name}: {function.name}Props)" ))
Convert inline prop type definitions to separate type declarations:
Copy
Ask AI
# Iterate over all files in the codebasefor file in codebase.files: # Iterate over all functions in the file for function in file.functions: # Check if the function is a React functional component if function.is_jsx: # Assuming is_jsx indicates a function component # Check if the function has inline props definition if len(function.parameters) == 1 and isinstance(function.parameters[0].type, Dict): # Extract the inline prop type inline_props: TSObjectType = function.parameters[0].type.source # Create a new type definition for the props props_type_name = f"{function.name}Props" props_type_definition = f"type {props_type_name} = {inline_props};" # Set the new type for the parameter function.parameters[0].set_type_annotation(props_type_name) # Add the new type definition to the file function.insert_before('\n' + props_type_definition + '\n')
This will convert components from:
Copy
Ask AI
function UserCard({ name, age }: { name: string; age: number }) { return ( <div> {name} ({age}) </div> );}
To:
Copy
Ask AI
type UserCardProps = { name: string; age: number };function UserCard({ name, age }: UserCardProps) { return ( <div> {name} ({age}) </div> );}
Extracting prop types makes them reusable and easier to maintain. It also
improves code readability by separating type definitions from component logic.
for function in codebase.functions: if not function.is_jsx: continue # Replace React.Fragment with <> for element in function.jsx_elements: if element.name == "React.Fragment": element.edit(element.source.replace( "<React.Fragment>", "<>" ).replace( "</React.Fragment>", "</>" ))
A common modernization task is splitting files with multiple components into a more maintainable structure where each component has its own file. This is especially useful when modernizing legacy React codebases that might have grown organically.
Copy
Ask AI
# Initialize a dictionary to store files and their corresponding JSX componentsfiles_with_jsx_components = {}# Iterate through all files in the codebasefor file in codebase.files: # Check if the file is in the components directory if 'components' not in file.filepath: continue # Count the number of JSX components in the file jsx_count = sum(1 for function in file.functions if function.is_jsx) # Only proceed if there are multiple JSX components if jsx_count > 1: # Identify non-default exported components non_default_components = [ func for func in file.functions if func.is_jsx and not func.is_exported ] default_components = [ func for func in file.functions if func.is_jsx and func.is_exported and func.export.is_default_export() ] # Log the file path and its components print(f"📁 {file.filepath}:") for component in default_components: print(f" 🟢 {component.name} (default)") for component in non_default_components: print(f" 🔵 {component.name}") # Create a new directory path based on the original file's directory new_dir_path = "/".join(file.filepath.split("/")[:-1]) + "/" + file.name.split(".")[0] codebase.create_directory(new_dir_path, exist_ok=True) # Create a new file path for the component new_file_path = f"{new_dir_path}/{component.name}.tsx" new_file = codebase.create_file(new_file_path) # Log the movement of the component print(f" 🫸 Moved to: {new_file_path}") # Move the component to the new file component.move_to_file(new_file, strategy="add_back_edge")
This script will:
Find files containing multiple React components
Create a new directory structure based on the original file
Move each non-default exported component to its own file
Preserve imports and dependencies automatically
Keep default exports in their original location
For example, given this structure:
Copy
Ask AI
components/ Forms.tsx # Contains Button, Input, Form (default)
It will create:
Copy
Ask AI
components/ Forms.tsx # Contains Form (default) forms/ Button.tsx Input.tsx
The strategy="add_back_edge" parameter ensures that any components that were
previously co-located can still import each other without circular
dependencies. Learn more about moving
code here.