[React] Refactor a Class Component with React hooks to a Fun
We have a render prop based?class component that allows us to make a GraphQL request with a given query string and variables and uses a GitHub graphql client that is in React context to make the request. Let‘s refactor this to a function component that uses the hooks useReducer,useContext,and useEffect. ? Class Based Component: import {Component} from ‘react‘ import PropTypes from ‘prop-types‘ import isEqual from ‘lodash/isEqual‘ import * as GitHub from ‘../../../github-client‘ class Query extends Component { static propTypes = { query: PropTypes.string.isRequired,variables: PropTypes.object,children: PropTypes.func.isRequired,normalize: PropTypes.func,} static defaultProps = { normalize: data => data,} static contextType = GitHub.Context state = {loaded: false,fetching: false,data: null,error: null} componentDidMount() { this._isMounted = true this.query() } componentDidUpdate(prevProps) { if ( !isEqual(this.props.query,prevProps.query) || !isEqual(this.props.variables,prevProps.variables) ) { this.query() } } componentWillUnmount() { this._isMounted = false } query() { this.setState({fetching: true}) const client = this.context client .request(this.props.query,this.props.variables) .then(res => this.safeSetState({ data: this.props.normalize(res),error: null,loaded: true,fetching: false,}),) .catch(error => this.safeSetState({ error,data: null,loaded: false,) } safeSetState(...args) { this._isMounted && this.setState(...args) } render() { return this.props.children(this.state) } } export default Query ? Conver props: // From static propTypes = { query: PropTypes.string.isRequired,} // To: function Query ({query,variables,children,normalize = data => data}) { } ? Conver Context: // From static contextType = GitHub.Context ... const client = this.context // To: import {useContext} from ‘react‘ function Query ({query,normalize = data => data}) { const clinet = useContext(GitHub.Context) } ? Conver State: I don‘t like to cover each state prop to ‘useState‘ style,it is lots of DRY,instead,using useReducer is a better & clean apporach. // From state = {loaded: false,error: null} //To: import {useContext,useReducer} from ‘react‘ ... const [state,setState] = useReducer( (state,newState) => ({...state,...newState}),defaultState) ? Conver side effect: // From: componentDidMount() { this._isMounted = true this.query() } componentDidUpdate(prevProps) { if ( !isEqual(this.props.query,) } // To: useEffect(() => { setState({fetching: true}) client .request(query,variables) .then(res => setState({ data: normalize(res),) .catch(error => setState({ error,) },[query,variables]) // trigger the effects when ‘query‘ or ‘variables‘ changes ? Conver render: // From: render() { return this.props.children(this.state) } // To: function Query({children ... }) { ... return children(state); } ? ----- ? Full Code: import {useContext,useReducer,useEffect} from ‘react‘ import PropTypes from ‘prop-types‘ import isEqual from ‘lodash/isEqual‘ import * as GitHub from ‘../../../github-client‘ function Query ({query,normalize = data => data}) { const client = useContext(GitHub.Context) const defaultState = {loaded: false,error: null} const [state,defaultState) useEffect(() => { setState({fetching: true}) client .request(query,variables]) // trigger the effects when ‘query‘ or ‘variables‘ changes return children(state) } export default Query (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |