1

My project use dvajs(Based on redux and redux-saga), The code below is to send a request after clicking the button, change the status through connect, and then call the ant design component message.error an message.success(Similar to alert) to remind

import type { Dispatch } from 'umi';
import ProForm, { ProFormText } from '@ant-design/pro-form';
import { message } from 'antd';

const tip = (type: string, content: string) => {
  if (type === 'error') message.error(content, 5);
  else message.success(content, 5);
};

const RegisterFC: React.FC<RegisterProps> = (props) => {
  const { registerResponseInfo = {}, submitting, dispatch } = props;
  const { status } = registerResponseInfo;

  const handleSubmit = (values: RegisterParamsType) => {
    dispatch({
      type: 'register/register',
      payload: { ...values },
   });
};      

  return (
    <div>
      <ProForm
         onFinish={(values) => {
            handleSubmit(values as RegisterParamsType);
            return Promise.resolve();
    }}
       >
       <ProFormText/>
       ...
      {
        status === '1' && !submitting && (
          tip('error',
            intl.formatMessage({
              id: 'pages.register.status1.message',
              defaultMessage: 'error'
            })
          )
        )
      }
    <<ProForm>/>
    </div>
  )
}

const p = ({ register, loading }: { register: RegisterResponseInfo, loading: Loading; }) => {
  console.log(loading);
  return {
    registerResponseInfo: register,
    submitting: loading.effects['register/register'],
  };
};

export default connect(p)(RegisterFC);

When I click the button, the console prompts:

Warning: Render methods should be a pure function of props and state; triggering nested component updates from render is not allowed. If necessary, trigger nested updates in componentDidUpdate.

Doesn't the component re-render when the state changes? Does the tip function change the state?

buzatto
  • 9,704
  • 5
  • 24
  • 33
Spring Li
  • 33
  • 5
  • The code that's here is incomplete and confusing. What is `p` and where is it called? How does `handleSubmit` access `dispatch`? (is it inside of a component?) I am getting a different error which is due to you printing the `void` results from `tip()` inside your `div`. `tip` updates the global `message` object but it does not return anything. – Linda Paiste Feb 21 '21 at 00:38
  • Sorry, I only intercepted part of the code due to too much code. I have made further improvements to the code. `p` is called by `connect` in the last line, `handleSubmit` should be inside the component. You mean to add `return` in `tip()`? – Spring Li Feb 21 '21 at 02:12
  • It's fine for `tip` to be `void` but you should be calling it outside of the component's `return` JSX. Somewhere in the body of the component, `if (status === '1' && !submitting) { tip(...); }` – Linda Paiste Feb 21 '21 at 02:16
  • Ok, but if I replace `tip(..)` with `FunctionComponent`, the warning will not appear. Why? code show as below `{ status === '0' && !submitting && ( } ` – Spring Li Feb 21 '21 at 02:23
  • `` is a JSX element that you are rendering to the DOM. The result of calling `tip()` is nothing. It is `void`. So you cannot render it. – Linda Paiste Feb 21 '21 at 02:39
  • I modify `tip() return jsx.element` and the warning disappeared. This way of writing is like conditional rendering. If the `tip()` is void, react will think that I am changing the state.You solved my doubts, thank you very much. – Spring Li Feb 21 '21 at 03:53

1 Answers1

0

Solution: Call tip Outside of return

tip is just a function that you are calling. You should call it outside of the return JSX section of your code. I think it makes the most sense to call it inside of a useEffect hook with dependencies on status and submitting. The effect runs each time that status or submitting changes. If status is 1 and submitting is falsy, then we call tip.

const RegisterFC: React.FC<RegisterProps> = (props) => {
    const { registerResponseInfo = {}, submitting, dispatch } = props;
    const { status } = registerResponseInfo;

    const handleSubmit = (values: RegisterParamsType) => {
        dispatch({
            type: 'register/register',
            payload: { ...values },
        });
    };

    React.useEffect(() => {
        if (status === '1' && !submitting) {
            tip('error',
                intl.formatMessage({
                    id: 'pages.register.status1.message',
                    defaultMessage: 'error'
                })
            );
        }
    }, [status, submitting]);

    return (
        <div>...</div>
    )
}

Explanation

Render methods should be a pure function of props and state

The render section of a component (render() in class component or return in a function component) is where you create the JSX (React HTML) markup for your component based on the current values of props and state. It should not have any side effects. It creates and returns JSX and that's it.

Calling tip is a side effect since it modifies the global antd messsage object. That means it shouldn't be in the render section of the code. Side effects are generally handled inside of useEffect hooks.

You are trying to conditionally render tip like you would conditionally render a component. The problem is that tip is not a component. A function component is a function which returns a JSX Element. tip is a void function that returns nothing, so you cannot render it.

Linda Paiste
  • 38,446
  • 6
  • 64
  • 102