2
\$\begingroup\$

Short Story: I created this website last year in plain HTML, CSS, JS which I can't even remember how I wrote that and now I can't even edit the website without breaking it. So, I'm re-writing everything in a proper way using React.


So, I'm making a website using React ( I'm still learning ) and I got confused about using CSS with it.

I have a TW component for type-writing animation like on the home and projects page.

You can see that same animation has different styles in home and projects page.

Previously, I have just used different CSS rules present in different webpages( with code redundant ). But doing same in React is not possible because of it's component approach.

Code:

typewriter.js

import React from 'react';
import { ICH } from '../individual-char-highlight/individual-char-highlight'; // When the mouse is hovered over a char it changes color
import b from '../css/blink.css'; // For cursor blinking effect
import s from './typewriter.css'; // Default style for this component



/**
 * By default, it returns a component which produces typewriter animation on the array of strings passed as `this.props.data`
 * *************************
 * *************************
 * Description of props:
 *
 * "Needed props"
 * name: `this.props.data[]`, type: Array(String)-> Produces typewriter animation on the array of strings passed
 * *************************
 * "Optional props"
 * name: `this.props.time`, type: Integer, default: 60 -> Milliseconds to delete or add one character to the screen
 *
 * name: `this.props.passTo`, type: String, default: undefined -> Used to pass the data to specified component after final string is constructed before it is displayed.
 */
export class TW extends React.Component {

    constructor(props) {
        super(props);

        // Initial state
        this.state = {
            data: ''
        };

        // - Mark methods
        this.animate = this.animate.bind(this);
        this.updateState = this.updateState.bind(this);
        this.passTo = this.passTo.bind(this);
        this.animate();

    }

    /**
     * animate - Typing Animation logic
     *
     */
    animate() {

        var dataIndex = 0; // It is used to iterate the `this.props.data`
        var d = this.props.data[dataIndex]; // Current string to apply animation
        var strLength = d.length; // Length of current string
        var text = this.props.data; // It is the string that changes to produce type writer effect
        var flag = true; // It is used to add or remove the character from `text`
        var index = strLength; // It is used in `substr` to specify the `to` argument and from is always `0`
        var count = 0; // It is used to delay the adding of first character when `text` is empty.

        setInterval(function () {

            if (text.length == 0) // If the string `text` is empty
            {
                if(count == 15) // Initially `0` waits till it gets to `15` and then adds the char
                {
                    dataIndex = (dataIndex >= this.props.data.length - 1) ? 0 : ++dataIndex;
                    d = this.props.data[dataIndex]; // Update the string to apply animation on
                    strLength = d.length; // Update the length of current string
                    index = 1; // Increment `index` to add the 1st char
                    flag = true; // Make it true so it adds chars one-by-one in next `else if`
                    count = 0; // Again make it `0` for next iteration

                }
                else
                {
                    count++; // Increment `count` when `text` is empty untill `count` = `15`
                }

            }
            else if (strLength + 20 == index) // same like `count`, `+20` delays the deletion after the `text` is full
            {
                index--; // Decrement `index` to delete the last char.
                flag = false; // Make it `false` so it delets chars one-by-one in next `else if`
            }
            else if (flag) // It executes to add the chars to `text` one-by-one
            {
                index++;
            }
            else if (!flag)  // It executes to delete the chars from `text` one-by-one
            {
                index--;
            }

            text = d.substr(0, index) // Display the string, even if it's empty
            this.updateState(text); // Call to update the `this.state.data`

        }.bind(this), this.props.time); // Change this number to change the typing speed. Less = Fast
    }

    /**
     * updateState - Updates the state with current `text` to call the render.
     *
     * @param  {String} text Displays the constructed `text` string
     *
     */
    updateState(text){

      this.setState({ // Display the constructed `text` string
          data: text,
      });
    }


    /**
     * passTo - Sends the constructed string to another component for further components if specified any. Otherwise returns unmodified string as component
     *
     * @return {span}  Span which might be modified by other components or not
     */
    passTo(){

      let mainData;
      // `style` is used to position the component at the center approximately due to cursor(which shifts to left)
      // //`style` is used for cursor span to position the cursor according to the `this.props.data`
      if(this.props.passTo == 'ICH'){
        mainData = [
                        <span className={s['data']}  style={{position:'relative',left:'5%'}}><ICH data = {this.state.data} name="typewriter" /></span>,
                        <span className={b['blink'] + ' ' + s['cursor']} style={{position:'relative',left:'5%'}}>&#9614;</span>
                   ]
      }
      else {
        mainData = [
                        <span className={s['data']}>{this.state.data}</span>,
                        <span className={b['blink'] + ' ' + s['cursor']}>&#9614;</span>
                   ]
      }

      return mainData;
    }

    render() {

      let displayData = this.passTo(); // Final span to be displayed after all the modifications

        return (
                <span>{displayData}</span>
        );
    }
}

/**
 * Default value of `this.props.time` is 60 milliseconds
 * @type {Object}
 */
TW.defaultProps = {time: 60};

Now I want to apply a style based on the webpage where it is used.


I thought of two options.

1) As you can see above, I'm using passTo() and this.props.passTo to send the data to another component for further animation. Similarly, I can send a style object from parent component as props and use the same above logic to apply the style as in-line using conditional statements.

2) Doing similar to option 1 but instead of passing style object, adding the styles in typewriter.css(which has default styles for this component) with className as the webpage name and applying it.

Which one is better? And is there a way to apply CSS from parent component?

Thank you!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.