| /** |
| * @fileoverview Prevent usage of setState in lifecycle methods |
| * @author Yannick Croissant |
| */ |
| |
| 'use strict'; |
| |
| const docsUrl = require('./docsUrl'); |
| |
| // ------------------------------------------------------------------------------ |
| // Rule Definition |
| // ------------------------------------------------------------------------------ |
| |
| function mapTitle(methodName) { |
| const map = { |
| componentDidMount: 'did-mount', |
| componentDidUpdate: 'did-update', |
| componentWillUpdate: 'will-update' |
| }; |
| const title = map[methodName]; |
| if (!title) { |
| throw Error(`No docsUrl for '${methodName}'`); |
| } |
| return `no-${title}-set-state`; |
| } |
| |
| function makeNoMethodSetStateRule(methodName, shouldCheckUnsafeCb) { |
| return { |
| meta: { |
| docs: { |
| description: `Prevent usage of setState in ${methodName}`, |
| category: 'Best Practices', |
| recommended: false, |
| url: docsUrl(mapTitle(methodName)) |
| }, |
| |
| schema: [{ |
| enum: ['disallow-in-func'] |
| }] |
| }, |
| |
| create(context) { |
| const mode = context.options[0] || 'allow-in-func'; |
| |
| function nameMatches(name) { |
| if (name === methodName) { |
| return true; |
| } |
| |
| if (typeof shouldCheckUnsafeCb === 'function' && shouldCheckUnsafeCb(context)) { |
| return name === `UNSAFE_${methodName}`; |
| } |
| |
| return false; |
| } |
| |
| // -------------------------------------------------------------------------- |
| // Public |
| // -------------------------------------------------------------------------- |
| |
| return { |
| |
| CallExpression(node) { |
| const callee = node.callee; |
| if ( |
| callee.type !== 'MemberExpression' || |
| callee.object.type !== 'ThisExpression' || |
| callee.property.name !== 'setState' |
| ) { |
| return; |
| } |
| const ancestors = context.getAncestors(callee).reverse(); |
| let depth = 0; |
| ancestors.some((ancestor) => { |
| if (/Function(Expression|Declaration)$/.test(ancestor.type)) { |
| depth++; |
| } |
| if ( |
| (ancestor.type !== 'Property' && ancestor.type !== 'MethodDefinition' && ancestor.type !== 'ClassProperty') || |
| !nameMatches(ancestor.key.name) || |
| (mode !== 'disallow-in-func' && depth > 1) |
| ) { |
| return false; |
| } |
| context.report({ |
| node: callee, |
| message: `Do not use setState in ${ancestor.key.name}` |
| }); |
| return true; |
| }); |
| } |
| }; |
| } |
| }; |
| } |
| |
| module.exports = makeNoMethodSetStateRule; |