重新开始

Get started with Redux Thunk

+$State is a big part of a React application, which is why Redux is commonly paired with it. That data often comes from a database, which requires a request and a response. For some applications, this communication can be constant. It can be tricky trying to manage solely within React components.

+$This also introduces some new problems – what happens if it loads slowly, or doesn't load at all? Every component dealing with asynchronous data would have to contain logic to handle these scenarios.

+$A 'thunk' is a concept that can help with this situation. Each thunk is a function that returns another function. That function can then be called at a later point, much like a callback. If we could dispatch a thunk instead of an action object, we can add in some extra logic in reaction to another event.

+$Redux Thunk is a library that sits in between dispatched actions and the reducer. When it sees a thunk is dispatched, it passes some methods into the returned function that can be used to dispatch further actions, like a success or error event.

+$In this tutorial, we will make use of thunks to help pull in the data from a server rather than from a JSON file, working with+$PhotoShare+$– a photo-commenting application powered by Redux.

下载文件本教程。

+$01. Install Dependencies

Get started with Redux Thunk: Install dependencies

+$Run both servers and leave them doing their stuff

+$There are two parts to this project – the front end site and the back end server it runs on. While the tutorial focuses on the front end, we need a server running in order to fetch the photos. Download the project files and install the dependencies for both the site and the server. Run the servers for both and leave them running in the background.

+$/* one terminal inside /site */ > yarn > yarn start /* one terminal inside /server */ > yarn > yarn start

+$02. Set Up Middleware

+$Redux Thunk is a+$middleware+$– functionality that sits between actions and reducers that can change how those actions behave. Redux supports multiple sets of middleware that cover the entire application. They get added when the store is created using the+$compose+$method. Add middleware to the+$createStore+$method within index.js.

+$import { applyMiddleware, compose } from "redux"; import thunk from "redux-thunk"; [...] const store = createStore( rootReducer, compose( applyMiddleware(thunk), devtools ) );

+$03. Set up action creators

+$The first thing we need to do now is to load the photos into the gallery. Like regular actions, we need action creators for the various states that an asynchronous call will take. Most will have开始成功错误+$actions. These let Redux know what the JavaScript is busy doing. Within+$actions/photos/photos.js+$, set up three action creators for these different states.

+$export const loadGalleryStart =() => ({ type: LOAD_GALLERY_START }); export const loadGallerySuccess = photos => ({ type: LOAD_GALLERY_SUCCESS, photos }); export const loadGalleryError =() => ({ type: LOAD_GALLERY_ERROR });

+$04. Create a thunk for loading

+$Thunks work exactly the same as action creators. We still dispatch the return value, but this time it returns a function instead of an object. The middleware that we set up earlier will pass a dispatch method into the returned function. This enables us to send more actions to Redux after the initial dispatch. Create a+$loadGallery+$method that returns a function. For now, have it dispatch an action to show that the gallery is still loading.

+$export const loadGallery = () => dispatch => { dispatch(loadGalleryStart()); };

+$05. Load data from the server

+$We are now ready to start fetching from the server we set up at the beginning. We can do this by using+$axios+$– a package designed to work with promises across different browsers. Import+$axios+$and make a request for the photos within+$loadGallery+$. If the promise resolves, dispatch the success action, and if not dispatch the error action. With that, the structure of the thunk is complete.

+$import axios from "axios"; [...] return axios .get("http://localhost:3001/photos") .then(response => dispatch( loadGallerySuccess(response.data))) .catch(() => dispatch( loadGalleryError()));

+$06. Dispatch The Thunk

Get started with Redux Thunk: Dispatch the thunk

+$An un-dispatched thunk is a useless hunk of junk

+$The thunk will not do anything until it's been dispatched. We can do that within a React component like any other action. A good time to start loading the photos is when the user views the main gallery. We can use React'sComponentDidMount+$lifecycle method as a trigger, after checking the gallery is not already loaded. Within+$components/container/Gallery/Gallery.js+$dispatch A+$loadGallery+$action by adding it to+$mapDispatchToProps+$and calling it withinComponentDidMount

+$componentDidMount() { if (!this.props.photosLoaded) { this.props.loadGallery(); } } export const mapDispatchToProps = dispatch => ({ loadGallery: () => dispatch(loadGallery()), });

+$07. Add photos on success

Get started with Redux Thunk: Add photos on success

+$Once photos have arrived, they're passed to the gallery component

+$When the photos come back from the server, we dispatch a+$LOAD_GALLERY_SUCCESS+$action with the photos. We need to get this into the state through the+$photos+$reducer. Head To+$reducers/photos/photos.js+$and add a case for the success action. The payload contains all the photos as an array. Once the state is updated, the photo selector passes the photos through to the gallery component to be displayed.

+$case LOAD_GALLERY_SUCCESS: return action.photos;

+$08. Set up the UI

+$Currently, the photos suddenly appear after they are loaded. On a slower connection, the user will be looking at a blank screen until the request finishes, if it ever does. The actions we send to load photos can also be picked up in the UI reducer in order to keep the interface up to date with what's happening. Update the loading and error flags within the UI reducer at+$reducers/ui/ui.js

+$case LOAD_GALLERY_ERROR: return { ...state, loading: false, error: true }; case LOAD_GALLERY_START: return { ...state, loading: true, error: false }; case LOAD_GALLERY_SUCCESS: return { ...state, loading: false };

+$09. Add loading and error selector

+$As with the gallery photos themselves, we need selectors to get the various UI states values out of Redux. We can pass these to the gallery, which will then render different elements if either one is true. In+$selectors/ui/ui.js+$, add a couple of functions to get the values out.

+$export const isGalleryErrored = state => state.ui.error; export const isGalleryLoading = state => state.ui.loading;

+$10. Add data to GalleryContainer

+$With the selectors ready, they can now be added to the画廊+$container component. Adding them here means that the component responsible for displaying the gallery does not need to know about how the data has arrived. Head to+$container/Gallery/Gallery.js+$and add the selectors to+$mapStateToProps+$. Make constants for the values to help display the state in the next step.

+$const { error, loading, photos } = this.props; [...] export const mapStateToProps = state => ({ error: isGalleryErrored(state), loading: isGalleryLoading(state), });

+$11. Show loading and error state

Get started with Redux Thunk: Show loading and error state

+$Make sure the error and loading components appear when needed

+$While we have the error and loading props, there is currently no UI to indicate when they are active. These props are Boolean values, which means we can toggle the display of components when they are true. Update the render method to make sure the+$+$+$components render instead of the gallery when needed.

+$if (error) { return ; } if (loading) { return ; }

+$With the gallery loaded, we can move on to an individual photo. Clicking into any of the photos and refreshing the page does not load the photo back up, as there is no instruction on this page yet to load the gallery. Open+$container/Photo/Photo.js+$and load the gallery inComponentDidMount+$like In The画廊+$component. The+$photosLoaded+$check will not try to load the photos again if they were already loaded within the gallery.

+$if (!this.props.photosLoaded) { this.props.loadGallery(); }

+$13. Add a new comment

Get started with Redux Thunk: Add a new comment

使用+$addNewComment+$prop function for adding comments

+$The user can click on the photo where they want to leave a comment. The Photo presentational component will run the+$addNewComment+$prop function when this happens. Inside the+$addNewComment+$function, calculate the point where the user has clicked within the photo. The server requires a round integer percentage value when it gets saved.

+$const photo = e.target .getBoundingClientRect(); const top = e.clientX - photo.left; const left = e.clientY - photo.top; const topPc = Math.round((top / photo.width) * 100); const leftPc = Math.round((left / photo.height) * 100);

+$14. Tell Redux about the comment

+$With the position calculated, we then need to tell Redux about the comment so it can display the comment form. There is already an action set up to add the new comment on screen. Add+$addNewComment+$mapDispatchToProps+$and call it after we calculated the position of the click.

+$this.props.addNewComment( topPc, leftPc); […] export const mapDispatchToProps = dispatch => ({ addNewComment: (top, left) => dispatch(addNewComment(top, left)), });

+$15. Tell Photo about new comment

+$When new comment information is passed to Redux, we need to pass it into the Photo presentational component. This enables it to show the form at that position. Find the+$getNewComment+$selector, add it to+$mapStateToProps+$and pass the prop into+$

+$export const mapStateToProps = (state, props) => ({ newComment: getNewComment(state), }); 

+$16. Call thunk in comment

Get started with Redux Thunk: Call thunk in comment

+$Create a thunk to add a comment to a photo

+$Clicking on the photo now will bring up the new comment form. This is its own connected component. When the form is submitted, it calls a+$submitComment+$prop function and gets passed. This is a thunk that we will make. Open up+$container/NewComment/NewComment.js+$and add the thunk to+$mapDispatchToProps+$. Pass that prop into the rendered presentational component.

+$ export const mapDispatchToProps = dispatch => ({ submitComment: comment => dispatch( submitComment(comment)) });

+$17. Gather content for thunk

+$The thunk to add a new comment has a similar structure to the fetching of the gallery, including a start, success and error action. There is an extra argument passed into this thunk – the+$getState+$function. This enables direct access to the current state in order to grab data from it. Create the+$submitComment+$thunk In+$actions/newComment/newComment.js+$. Each comment is associated with a photo and a user. For this tutorial, the user ID is hard-coded into the用户+$reducer.

+$export const submitComment = comment => (dispatch, getState) => { dispatch(submitCommentStart()); const currentPhotoId = getCurrentPhotoId(getState()); const user = getCurrentUser(getState()); const { left, top } = getNewComment(getState()); };

+$18. Post The Request

+$With all the necessary data in place, we can submit the comment. Axios has a岗位+$method to deal withPOST+$requests, with the second argument being the data to send in that request. Add the request to the thunk, passing in data in snake case to match what the server expects.

+$return axios .post( "http://localhost:3001/comments", { user_id: user.id, photo_id: currentPhotoId, comment, left, top })

+$19. Handle success and error

+$If the promise from axios resolves or rejects, we need to tell the application about it. If it resolves successfully, the server will pass back the content of the comment. We should pass that in with the success action. If it gets rejected, fire an error action. Update the promise with然后抓住+$blocks.

+$.then(({ data: { id, comment, left, top } }) => dispatch( submitCommentSuccess( id, comment, left, top, user, currentPhotoId) ) ) .catch(() => dispatch( submitCommentError()));

+$20. Add comment to photo

Get started with Redux Thunk: Add comment to photo

+$Edit the photos reducer so that comments appear immediately

+$Right now, once the comment is added successfully it gets cleared from the screen but is not visible until the page refreshes. We can update the photos reducer to pick up on the new comment and add it to its comments array, to display like the rest of them. Open up reducer/photos/photos.js and add a case to handle the action. Create a copy of the state to make sure we don't accidentally mutate the existing state.

+$case SUBMIT_COMMENT_SUCCESS: const { id, comment, top, left, user, photoId } = action.payload; const newState = JSON.parse( JSON.stringify(state)); const photo = newState.find( photo => photo.id === photoId); photo.comments.push({ id, comment, left, top, user }); return newState;

+$21. Hide Other Comments

+$Lastly, if another comment is open and the user wants to add a new comment, the UI gets too cluttered. We should hide the comment box if a new comment is being composed. We can hook into the existing+$ADD_NEW_COMMENT+$action to clear the+$commentOpen+$value. Head To+$reducer/ui/ui.js+$and add a case for that.

+$case ADD_NEW_COMMENT: return { ...state, commentOpen: undefined };

这篇文章最初发表在“创意网页设计”杂志第283期。网页设计者在这里购买问题283要么在这里订阅Web Designer

相关文章:



翻译字数超限