Tutorial Setting up route based transitions, Creating our First Custom Component,Dynamically Fetching Authors in CSS-in-JS React App
Repository
https://github.com/facebook/react
What Will I Learn?
I will learn :
- Setting up route based transitions within our React application.
- Creating our First Custom Component
- Dynamically Fetching Authors.
Requirements
- https://nodejs.org/
- https://npmjs.com/
- https://yarnpkg.com/
- https://github.com/
- https://reactjs.org/
Difficulty
- Advanced
Setting up Route Based Transitions
We can do this by using the transition group properties present within React Router. We'll be adding a fade transition for our app. Let's head over to src/containers/App/index.js
and get to work. For starters, we'll import the TransitionGroup component from react-transition-group.
We also need to import the yet to be created FadeTransition component from
src/components/Transitions/fade.js
import FadeTransition from "../../components/Transitions/fade";
Next, we'll update the render method of the App class to use our imports. We wrap everything in our TransitionGroup and we also use our to be created FadeTransition component.
<FadeTransition key={location.key}>
<ThemeProvider>
<Flex flexDirection="column" className="App" style={{ minHeight: '100vh' }}>
<Header/>
<Route exact path="/" component={LoadableHomeScreen} />
<Route path="/add" component={LoadableAddLineScreen} />
</Flex>
</ThemeProvider>
</FadeTransition>
Next, let's create
src/components/Transitions/fade.js
and add some code. We'll be basically wrapping the children components of this class within react-transition-group's CSSTransition method. We also specify a timeout and a CSS class for it.
import { CSSTransition } from "react-transition-group";
export const FadeTransition = ({ children, ...props }) => {
return (
<CSSTransition {...props} timeout={200} classNames="fade">
{children}
</CSSTransition>
);
};
export default FadeTransition;
Finally, we'll add a little CSS to src/index.js just for effect.
.fade-enter {
opacity: 0;
z-index: 1;
}
.fade-enter.fade-enter-active {
opacity: 1;
transition: opacity 250ms ease-in;
}
</style>
Creating our First Custom Component
In our AddLine class, we imported the Textarea class which we are yet to create. Let's handle that now. Create src/components/Form/Textarea.js
and we'll get started. First, I am import our dependencies. We are using the space and theme methods from styled system. This will give us access to the default spacings and theme setting that come with styled system.
import styled from 'styled-components';
import { space, theme } from 'styled-system'
Next, I do something of great importance, I define a function that returns the styles for the borders that will surround our Text box. This function also accepts the color and theme objects and makes them accessible to us. I am also write code that provide defaults in the event that the function received no arguments. I am then return an object with our styles intact.
const borderColor = color ? theme.colors[color] : theme.colors.borderGray
const focusColor = color ? borderColor : theme.colors.gray
return {
'border-color': borderColor,
'box-shadow': `0 0 0 1px ${borderColor}`,
':focus': {
outline: 0,
'border-color': focusColor,
'box-shadow': `0 0 0 2px ${focusColor}`
}
}
}
Next, I am define the styles for our Textarea in a new ES6 proposal called 'tagged template literals'. We write regular CSS here except for the theme function calls. Calling the theme method with an argument here will return a value.
appearance: none;
display: block;
width: 100%;
font-family: inherit;
color: inherit;
font-size: ${theme('fontSizes.1')}px;
background-color: transparent;
border-radius: ${theme('radius')};
border-width: 0px;
border-style: solid;
border-color: ${theme('colors.borderGray')};
padding-top: 14px;
padding-bottom: 14px;
padding-left: 12px;
padding-right: 12px;
margin: 0;
::placeholder {
color: ${theme('colors.gray')};
}
::-ms-clear {
display: none;
}
${borders} ${space};
'
Fire up your browser and head to http://localhost:300/add
Dynamically Fetching Authors.
We'd like to add a means of keeping our authors list dynamic. This is important, because along in this series, we'll be adding functionality that allows us to create authors dynamically.
- Game Plan
We need to fetch our authors immediately our app starts. This means using a lifecycle event method to run the action that fetches our authors. That means we'll be modifying the App root component a little to allow us add new behavior.
Let's edit src/containers/App/index.js
and get to work. We'll be adding some new dependencies. We'll be using the withRouter method from react-router as we'd like to be able to access some router based properties within our component. We'll also be importing the fetchAuthorsRequest from the Home screen actions.
import { connect } from 'react-redux';
import { fetchAuthorsRequest } from '../../screens/Home/actions';
We'll need to add the componentDidMount lifecycle method. We'll be calling this.props.fetchAuthors
in this method. We'll define this method below.
componentDidMount() {
this.props.fetchAuthors()
}
Next, we'll run the usual mapStateToProps
and mapDispatchToProps
where we get to define the fetchAuthors
method that will make the request to our saga telling it, "Hey, we want all the authors you've got".
return {}
}
const mapDispatchToProps = (dispatch) => {
return {
fetchAuthors: data => dispatch(fetchAuthorsRequest(data))
}
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(App));
Next, we must define the recently used fetchAuthorsRequest
action dispatcher. We'll add the fetchAuthorsRequest
method to the actions.js
file at src/screens/Home
We're simply importing a couple of constants and we then define our action object. This will dispatch the ADD_AUTHORS_REQUEST
action to our saga.
export const fetchAuthorsRequest = () => {
return {
type: ADD_AUTHORS_REQUEST
}
}
Next, we'll add a saga for this request. We'll define the fetchAuthors generator function at src/screens/Home/saga.js
We'll start off by importing the getAuthorsData method from the Data Service we created previously. We also import a couple of constants we'll be using. We then use the special call method provided by Redux-Saga to resolve the promise returned by the getAuthorsData method. We then call the put method providing it with some required parameters (type and payload).
import { ADD_AUTHORS_REQUEST } from './constants';
import { SET_AUTHORS_DATA } from '../../containers/App/constants';
export function* fetchAuthors (payload) {
const response = yield call(getAuthorsData);
return yield put({
type: SET_AUTHORS_DATA,
payload: {
data: response
}
})
}
We'll quickly define the imported constants. Editing src/containers/App/constants.js
Also editing src/screens/Home/constants.js.
``js
export const ADD_AUTHORS_REQUEST = 'app/ADD_AUTHORS_REQUEST';
We'll be defining the getAuthorsData
method at the Data Service. Let's edit src/services/DataService/index.js
. We'll be creating the authors.json
file at src/assets/data
shortly. In the meantime, we import the JSON data from it, and we resolve that data in a promise that we return.
export const getAuthorsData = (id = null) => {
if (id) {
let author = authorsData.filter(author => author.id === id);
return new Promise(resolve => resolve(author));
}
return new Promise(resolve => resolve(authorsData));
};
It's now time to create our authors.json along with the information. Let's use this file as a base to build on. Create src/assets/data/authors.json
and add test data.
"name": "Immortal Technique",
"government_name": "Sara Youlist Bust",
"photo": "immortal-technique.jpg"
},
{
"name": "Sarayulis",
"government_name": "Sara Youlist",
"photo": "yulis.jpg"
},
{
"name": "Sara 3000",
"photo": "sara-3000.jpg"
}]
Great! All we need to do now is add a reducer for this action. We'll start by importing the SET_AUTHORS_DATA
constant. We then have the authors property of the initial state as being an empty object. We check for our SET_AUTHORS_DATA
action and if our action was called we simply set the erstwhile empty authors property in the initialState array to the action's payload data.
const initialState = fromJS({
// ...Previous code here.
authors: []
})
const AppReducer = (state = initialState, action) => {
switch (action.type) {
case SET_AUTHORS_DATA:
return state.set('authors', action.payload.data)
// ...Previous code goes here.
default:
return state
}
}
Heading back to our browser tab at http://localhost:3000, we see our list of authors show up in the select box. That's awesome! If we hit the submit button, we get to see our new entry with the selected author.
Resources
- https://github.com/jamiebuilds/react-loadable
- https://reacttraining.com/react-router/web/guides/basic-components
Thank you for your contribution.
While I liked the content of your contribution, I would still like to extend few advices for your upcoming contributions:
Tutorial content: There are parts of the code that have little explanation, try to explain as much as possible.
Structure of the tutorial: Improve the structure of the tutorial.
Try to come up with new and more innovative/useful ways to utilize React.
Looking forward to your upcoming tutorials.
Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.
To view those questions and the relevant answers related to your post, click here.
Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]
Okey sir @portugalcoin Thank you for your advice, I will be better in the future.
Please review comment on your other post, confirming ban of your account from receiving Utopian Reviews.
Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]