/** * @fileoverview Prevent usage of unsafe lifecycle methods * @author Sergei Startsev */ 'use strict'; const Components = require('../util/Components'); const astUtil = require('../util/ast'); const docsUrl = require('../util/docsUrl'); const versionUtil = require('../util/version'); // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ module.exports = { meta: { docs: { description: 'Prevent usage of unsafe lifecycle methods', category: 'Best Practices', recommended: false, url: docsUrl('no-unsafe') }, messages: { unsafeMethod: '{{method}} is unsafe for use in async rendering. Update the component to use {{newMethod}} instead. {{details}}' }, schema: [ { type: 'object', properties: { checkAliases: { default: false, type: 'boolean' } }, additionalProperties: false } ] }, create: Components.detect((context, components, utils) => { const config = context.options[0] || {}; const checkAliases = config.checkAliases || false; const isApplicable = versionUtil.testReactVersion(context, '16.3.0'); if (!isApplicable) { return {}; } const unsafe = { UNSAFE_componentWillMount: { newMethod: 'componentDidMount', details: 'See https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html.' }, UNSAFE_componentWillReceiveProps: { newMethod: 'getDerivedStateFromProps', details: 'See https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html.' }, UNSAFE_componentWillUpdate: { newMethod: 'componentDidUpdate', details: 'See https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html.' } }; if (checkAliases) { unsafe.componentWillMount = unsafe.UNSAFE_componentWillMount; unsafe.componentWillReceiveProps = unsafe.UNSAFE_componentWillReceiveProps; unsafe.componentWillUpdate = unsafe.UNSAFE_componentWillUpdate; } /** * Returns a list of unsafe methods * @returns {Array} A list of unsafe methods */ function getUnsafeMethods() { return Object.keys(unsafe); } /** * Checks if a passed method is unsafe * @param {string} method Life cycle method * @returns {boolean} Returns true for unsafe methods, otherwise returns false */ function isUnsafe(method) { const unsafeMethods = getUnsafeMethods(); return unsafeMethods.indexOf(method) !== -1; } /** * Reports the error for an unsafe method * @param {ASTNode} node The AST node being checked * @param {string} method Life cycle method */ function checkUnsafe(node, method) { if (!isUnsafe(method)) { return; } const meta = unsafe[method]; const newMethod = meta.newMethod; const details = meta.details; context.report({ node, messageId: 'unsafeMethod', data: { method, newMethod, details } }); } /** * Returns life cycle methods if available * @param {ASTNode} node The AST node being checked. * @returns {Array} The array of methods. */ function getLifeCycleMethods(node) { const properties = astUtil.getComponentProperties(node); return properties.map((property) => astUtil.getPropertyName(property)); } /** * Checks life cycle methods * @param {ASTNode} node The AST node being checked. */ function checkLifeCycleMethods(node) { if (utils.isES5Component(node) || utils.isES6Component(node)) { const methods = getLifeCycleMethods(node); methods.forEach((method) => checkUnsafe(node, method)); } } return { ClassDeclaration: checkLifeCycleMethods, ClassExpression: checkLifeCycleMethods, ObjectExpression: checkLifeCycleMethods }; }) };