Scroll effects with react and styled-components
Tags: react, styledcomponents
Published: 2020-05-24 15:16:16

In this tutorial, we will make a react-app that will make a cog that rotates based on a user scrolling. It will scroll one way when a user scrolls down and the other when a user scrolls up. 

If you think this is really cool, please share and give this post a heart. 

You will learn how to add dynamic values to css attributes with styled-components based on user events. This is where you blur the line between engineering and art. 

To ensure I'm not waste your time, here is the [github](https://github.com/TallanGroberg/scroll-tutorial)

here is the [site](https://scroll-tutorial.surge.sh/)

__Why is this useful?__

Knowing how to add tasteful animations to your websites will impress your users.

It will be artistically fulfilling to command this much more imagination over the art of web development.

It could potentially be profitable and 'wow' a client or make your portfolio that much better.

Prerequisites: 

1. [create-react-app](https://www.npmjs.com/package/create-react-app)

2. Basic knowledge of [react](https://reactjs.org/), [styled-components](https://www.npmjs.com/package/styled-components) and [javascript](https://javascript.info/).

3. Basic [command-line for installing npm packages](https://www.tutorialsteacher.com/nodejs/what-is-node-package-manager) I'll be using bash command-line

To get started make a new react-app called scroll-tutorial 



```bash

create-react-app scroll-tutorial
```

open that file directory 

```javascript
cd scroll-tutorial
```

Open with the text editor of your choice. I use [VScode](https://code.visualstudio.com/download).

```bash
code . 
```

Now let's install styled-components.

```bash
npm i styled-components
```

There may be another way to make this work but the way I found easiest was to create context and capture the scroll position with an event listener and wrap the styled div in context to add the number. 

Inside the src folder, let's create a folder called provider and file called ThemeProvider where context will be.

Right-click the src folder. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/90hzms87mg23jlueliam.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/3dvu35xuzo3xfj6ews0r.png)

Now make the file. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/a1h1ouc7gy4xn0wj1eqe.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ggs0ypqz2bbpj8v64oax.png)

You can copy and paste this to make the skeleton for the file. 

```javascript
import React from 'react';

// context variable goes here


const ThemeProvider = () => {
  return (
    <>
      
    </>
  );
};

//higher order component goes here.


export default ThemeProvider;
```

We can destructure the context variable since we aren't going to be using the useContext hook and a higher-order component instead. 

```javascript
// context variable goes here

const {Provider,  Consumer } = React.createContext()
```

Now inside the <> also called fragments add the Provider like so. 

```javascript
return (
    <Provider>
      
    </Provider>
  );
``` 

Add the value object to the opening Provider tag and props.children.



```javascript
    <Provider value={{
    
    }}>
      {props.children}
    </Provider>
```
__Don't forget to add props at the top of the react component__

```javascript
const ThemeProvider = (props) => {
``` 

We are going to be saving the scroll event to state, so at the top of the file, add useState to the react import. 

```javascript
import React, {useState} from 'react';
```

Make a piece of state to save the scroll position inside the react component at the top. 

```javascript
const [scrollPosition, setScrollPosition] = useState(0)
```
Add the scroll postion into the the value object. 

```javascript
 <Provider value={{
      scrollPosition,
    }}>
```

The best practice is to make sure that context is working before we start making the functionality for it. this means that we will make the consumer, make sure we have a scroll position in the App.js then add the event listener for user scroll events. 

Below and outside the component we are going to make the consumer for this provider. 

This will be a HOC or higher-order component. 

I highly recommend learning more about functional programming paradigms and closures but the details go beyond the _scope_ of this tutorial. (pun intended.)

Let's make the skeleton of this function. 

```javascript
//higher order component goes here.
export const withTheme = C => props => ()
```
Inside the parenthisis add the Consumer like so. 

```javascript
export const withTheme = C => props => (
  <Consumer>
    
  </Consumer>
)
```

Inside the Consumer add the value and the consumer and make so that everything we pass to the withTheme function will be a child to this function. 

The whole function should look like this. 

```javascript
//higher order component goes here.
export const withTheme = C => props => (
  <Consumer>
    {value => <C {...value} {...props} /> }
  </Consumer>
)
```

The whole file should look like this. 

```javascript
import React, {useState} from 'react';

// context variable goes here

const {Provider,  Consumer } = React.createContext()

const ThemeProvider = (props) => {
  const [scrollPosition, setScrollPosition] = useState(0)

  return (
    <Provider value={{
      scrollPosition,
    }}>
      {props.children}
    </Provider>
  );
};

//higher order component goes here.
export const withTheme = C => props => (
  <Consumer>
    {value => <C {...value} {...props} /> }
  </Consumer>
)

export default ThemeProvider;
```
_ThemeProvider.js_

Now go to the index.js and wrap your app with the provider. 

```javascript
import ThemeProvider from './provider/ThemeProvider'

ReactDOM.render(
  <React.StrictMode>
    <ThemeProvider>
      <App />
    </ThemeProvider>
  </React.StrictMode>,
  document.getElementById('root')
);
```
_index.js_

In the App.js add props and and console.log them.

```javascript
function App(props) {
  console.log(props)
```

With VS code press control + ~ , the button underneath the escape key and in the teminal that pops up. run npm start. 

```bash
npm start 
```
_command line_

The console will return an empty object. 

To give us the scrollPosition, import withTheme at the top of our App.js

```javascript
import {withTheme} from './provider/ThemeProvider'
```

At the bottom where the export is wrap the App withTheme. 

```javascript
export default withTheme(App);
```
We should now see a key-value pair with a scrollPosition of 0. 

Let's import styled components and make a styled container while we are here. 

```javascript
import styled from 'styled-components'
```

At the bottom make a styled Container.

```javascript
const Container = styled.div``
```

Replace the react boiler plate with an empty Container.

```javascript
return (
    <Container>
      
    </Container>
  );
```

Follow the link and [download the one I used for this tutorial](). 

Right-click on the image

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/bopty6qgypf4fawwd8u3.png)

Save it inside the src folder, change the name to cog.png. 

Import the image to the App.js

```javascript
import cog from './cog.png'
```

Add an img tag with the image inside the Container. 

```javascript
    <Container>
      <img src={cog} />
    </Container>
```

__Note: __ You may have to shop around for the cog you want to use. I looked up transparent cog icons on google and found something that I liked. the only requirement is that it is the background has to be transparent. 

Before we go back to the ThemeProvider, lets set up some CSS for our Container. 

First make the height 400vh.

Give the cog an id.

```javascript
 <img id="cog" src={cog} />
```

Give cog a position of fixed. 

```css
 #cog {
    position: fixed;
  }
```

The same way that we wrapped the App withTheme do that to the styled.div

```javascript
const Container = withTheme(styled.div`
height: 400vh;

  #cog {
    position: fixed;
  }
`)
```
Now our CSS has access to stateful logic. 

Make a transform: rotate() attribute on the #cog

```css
#big-cog {
    position: fixed;
    transform: rotate(0deg);
  }
```

To make it so that it will rotate on scrolling we have to go back to the provider and make the event listener. 

Between the return and the useState, add this event listener. 

```javascript
document.addEventListener('scroll', () => {
    console.log(window.scrollY)
  })
```
_ThemeProvider.js_

When you scroll with the console open you will see a lot of numbers indicating the vertical scroll position.

Now setScrollPosition to the window.scrollY

```javascript
document.addEventListener('scroll', () => {
    console.log(window.scrollY)
    setScrollPosition(window.scrollY)
  })
``` 

One last thing. we have to connect the number of degrees to be the number of scrollY to do that go back to the App.js and use this from props inside Containers props to be the number of degrees.

We can do this with template literal notation because thats exactly what css is written inside of with styled components.  

```javascript
transform: rotate(${props => props.scrollPosition}deg)
```

Check it out!! 

To make the scroll a little slower we can divide the number like this. 

```css
  transform: rotate(${props => props.scrollPosition / 20}deg)
```

I would like to add that this is great as an art piece by this does cause a lot of rerendering and may not be suitable for bigger apps 

this is the [github](https://github.com/TallanGroberg/scroll-tutorial)

this is a [site I made](https://scroll-tutorial.surge.sh/) that adds more cogs to look like they are working together.

If you would like me to show you how to add more cogs and position them I would be happy to do so!!